AGENTS.md
# Development Fundamentals
## Overview
This skill covers the foundational knowledge every software developer should command — drawn from canonical published works and industry-proven practices. It spans from low-level algorithms through code craftsmanship to system-level architecture.
## Knowledge Map
```
┌─────────────────────────────────────────────────────────┐
│ Architecture │
│ Microservices, Monoliths, DDD, Event-Driven, │
│ Hexagonal, Well-Architected Frameworks │
├─────────────────────────────────────────────────────────┤
│ Frontend │ Backend │
│ SPA, PWA, Micro-frontends, │ Data Modeling, API │
│ SSR, Islands Architecture │ Design, Caching, Auth │
├─────────────────────────────────────────────────────────┤
│ Integration Patterns │ Design Patterns │
│ EIP: Messaging, Routing, │ GoF: Creational, │
│ Transformation, Endpoints │ Structural, Behavioral │
├─────────────────────────────────────────────────────────┤
│ Algorithms & Data Structures │
│ Sorting, Searching, Graphs, DP, Combinatorial │
├─────────────────────────────────────────────────────────┤
│ Craftsmanship │
│ Clean Code, Clean Architecture, SOLID, 12-Factor, │
│ Refactoring, Boy Scout Rule │
└─────────────────────────────────────────────────────────┘
```
## Canonical Works
| Book | Author | Covers |
|------|--------|--------|
| *Design Patterns* | Gamma, Helm, Johnson, Vlissides (GoF) | 23 object-oriented patterns |
| *Enterprise Integration Patterns* | Hohpe & Woolf | Messaging, routing, transformation |
| *The Art of Computer Programming* | Donald Knuth | Algorithms, data structures, combinatorics |
| *Clean Code* | Robert C. Martin | Naming, functions, formatting, comments |
| *Clean Architecture* | Robert C. Martin | Dependency rule, boundaries, layers |
| *Refactoring* | Martin Fowler | Code smells, refactoring catalog |
| *Domain-Driven Design* | Eric Evans | Bounded contexts, aggregates, ubiquitous language |
| *Building Microservices* | Sam Newman | Service decomposition, communication, deployment |
| *The Pragmatic Programmer* | Hunt & Thomas | Career, approach, tools, pragmatic philosophy |
| *The Twelve-Factor App* | Adam Wiggins (Heroku) | Cloud-native application methodology |
| *Release It!* | Michael Nygard | Stability patterns, capacity, deployment |
| *Fundamentals of Software Architecture* | Richards & Ford | Architecture styles, characteristics, decisions |
## Choosing the Right Pattern Category
| Problem | Look In |
|---------|---------|
| Object creation complexity | Design Patterns → Creational |
| Composing objects / adapting interfaces | Design Patterns → Structural |
| Object communication / state management | Design Patterns → Behavioral |
| Service-to-service messaging | Integration Patterns |
| Algorithm selection / optimization | Algorithms |
| Code readability and maintainability | Craftsmanship |
| System decomposition and boundaries | Architecture |
| Client-side application structure | Frontend |
| Server-side data and API structure | Backend |
## Best Practices
- Learn patterns as a vocabulary, not a checklist — apply them when the problem calls for it, not preemptively.
- Start with the simplest architecture that works (monolith), evolve toward complexity (microservices) only when you have evidence you need it.
- Apply the Boy Scout Rule: leave code better than you found it, every time you touch it.
- Use SOLID principles as guardrails for daily decisions, not just for greenfield design.
- Prefer composition over inheritance — most GoF patterns are variations of this principle.
- Study algorithms for problem-solving intuition, not memorization — know when to reach for a graph algorithm vs. dynamic programming.
- Keep integration patterns in mind whenever systems need to communicate — messaging solves problems that synchronous calls create.
algorithms/AGENTS.md
# Algorithms & Data Structures
## Overview
This skill covers the foundational principles of algorithmic thinking, drawn primarily from Donald Knuth's *The Art of Computer Programming* (TAOCP). It provides guidance on analyzing algorithm efficiency, understanding complexity classes, and choosing the right algorithmic approach for a given problem.
## Canonical Reference
| Volume | Title | Covers |
|--------|-------|--------|
| TAOCP Vol. 1 | *Fundamental Algorithms* | Data structures, mathematical foundations, information structures |
| TAOCP Vol. 2 | *Seminumerical Algorithms* | Random numbers, arithmetic, floating-point |
| TAOCP Vol. 3 | *Sorting and Searching* | Sorting, searching, comparison of methods |
| TAOCP Vol. 4A | *Combinatorial Algorithms, Part 1* | Combinatorial generation, backtracking, constraint satisfaction |
| TAOCP Vol. 4B | *Combinatorial Algorithms, Part 2* | Satisfiability, graph algorithms |
## Big-O Notation
Big-O describes the upper bound of an algorithm's growth rate as input size increases. It characterizes the worst-case behavior and allows comparison between algorithms independent of hardware.
### Common Complexity Classes
| Notation | Name | Example |
|----------|------|---------|
| O(1) | Constant | Hash table lookup, array index access |
| O(log n) | Logarithmic | Binary search, balanced BST lookup |
| O(n) | Linear | Linear search, single array traversal |
| O(n log n) | Linearithmic | Mergesort, Heapsort, efficient comparison sorts |
| O(n^2) | Quadratic | Bubble sort, insertion sort (worst case), nested loops |
| O(2^n) | Exponential | Recursive Fibonacci (naive), subset enumeration |
### Growth Rate Comparison
```
n O(1) O(log n) O(n) O(n log n) O(n^2) O(2^n)
1 1 0 1 0 1 2
10 1 3.3 10 33 100 1,024
100 1 6.6 100 664 10,000 ~1.27 x 10^30
1,000 1 10 1,000 10,000 1,000,000 ~1.07 x 10^301
10,000 1 13.3 10,000 133,000 100,000,000 (infeasible)
```
## Space vs Time Tradeoffs
Every algorithm makes a tradeoff between how much memory it uses and how fast it runs. Key principles:
- **Caching / Memoization**: Use extra space to store computed results and avoid redundant work (trades space for time).
- **In-place algorithms**: Minimize space usage at the cost of potentially more complex logic or slower execution (trades time for space).
- **Lookup tables**: Precompute results and store them for O(1) access (trades space for time).
- **Compression**: Reduce space at the cost of encoding/decoding time (trades time for space).
| Strategy | Space | Time | Example |
|----------|-------|------|---------|
| Memoized recursion | O(n) extra | Avoids recomputation | DP Fibonacci |
| In-place sort | O(1) extra | May be slower | Heapsort vs Mergesort |
| Hash table | O(n) extra | O(1) average lookup | Two-sum problem |
| Bit manipulation | O(1) extra | Constant factor overhead | Flags, compact sets |
## Amortized Analysis
Amortized analysis averages the cost of operations over a sequence, even when individual operations may be expensive. It provides a tighter bound than worst-case analysis for data structures that occasionally restructure.
- **Aggregate method**: Total cost of n operations divided by n.
- **Accounting method**: Assign different charges to different operations; overcharges on cheap operations "pay" for expensive ones.
- **Potential method**: Define a potential function on the data structure state; amortized cost = actual cost + change in potential.
**Example**: Dynamic array (ArrayList) doubling. Individual insertions are O(1) amortized even though resizing copies all elements, because resizing happens infrequently (the cost of copying is spread across the insertions that preceded it).
## Choosing Algorithms by Problem Type
| Problem Type | Recommended Approach | Sub-Skill |
|--------------|---------------------|-----------|
| Ordering elements | Comparison sort (Quicksort, Mergesort) or linear sort (Radix) | sorting-searching |
| Finding elements | Binary search, hash-based lookup | sorting-searching |
| Storing/retrieving structured data | Choose appropriate data structure by access pattern | data-structures |
| Shortest path / connectivity | Graph algorithms (BFS, DFS, Dijkstra) | graph-algorithms |
| Optimization with overlapping subproblems | Dynamic programming | dynamic-programming |
| Enumerating configurations / constraint solving | Backtracking, branch and bound | combinatorial |
| String matching | KMP, Rabin-Karp, suffix structures | sorting-searching |
| Scheduling / ordering dependencies | Topological sort | graph-algorithms |
| Minimum spanning tree | Prim's, Kruskal's | graph-algorithms |
| Subset/permutation generation | Combinatorial generation | combinatorial |
## Algorithm Analysis Checklist
When evaluating or selecting an algorithm:
1. **Identify the problem class** -- Is it a searching, sorting, graph, optimization, or enumeration problem?
2. **Determine input constraints** -- What is the expected input size? Are there special properties (sorted, sparse, bounded range)?
3. **Analyze time complexity** -- What is the worst-case, average-case, and best-case behavior?
4. **Analyze space complexity** -- How much auxiliary memory is required?
5. **Consider stability and determinism** -- Does order preservation matter? Is randomness acceptable?
6. **Evaluate practical constants** -- Two O(n log n) algorithms may differ significantly in constant factors and cache behavior.
7. **Benchmark with real data** -- Asymptotic analysis is a starting point; real-world performance depends on data distribution and hardware.
## Best Practices
- Start with the simplest correct algorithm, then optimize if profiling shows a bottleneck.
- Know the standard library -- most languages provide well-optimized sorting, searching, and data structure implementations.
- Prefer algorithms with good average-case behavior for general use; consider worst-case guarantees for safety-critical systems.
- Understand amortized costs before concluding that an operation is "slow" based on a single invocation.
- Reference Knuth's TAOCP for rigorous analysis and historical context on any fundamental algorithm.
algorithms/combinatorial/AGENTS.md
# Combinatorial Algorithms
## Overview
Combinatorial algorithms deal with counting, generating, and optimizing discrete structures -- permutations, combinations, subsets, partitions, and arrangements subject to constraints. Knuth devoted *The Art of Computer Programming, Volume 4A: Combinatorial Algorithms, Part 1* entirely to this topic, calling combinatorics "the mathematics of choice." This skill covers the core techniques for systematic enumeration, backtracking search, and constraint satisfaction.
## Fundamental Counting
### Permutations
An ordered arrangement of n elements. The number of permutations of n distinct elements is n! (n factorial).
- **All permutations of n elements**: n!
- **k-permutations of n elements** (ordered selection of k from n): n! / (n - k)!
- **Permutations with repetition**: n! / (n1! * n2! * ... * nk!) where ni is the count of each repeated element.
### Combinations
An unordered selection of k elements from n. The number of combinations is C(n, k) = n! / (k! * (n - k)!).
- **With repetition** (multiset coefficient): C(n + k - 1, k)
### Subsets
The power set of a set with n elements has 2^n subsets. Each element is either included or excluded.
## Backtracking
Backtracking is a systematic method for generating all (or some) possible configurations of a search space by incrementally building candidates and abandoning a candidate ("backtracking") as soon as it is determined that it cannot lead to a valid solution.
### Backtracking Template
```
BACKTRACK(state, choices):
if state is a complete solution:
process(state)
return
for each choice in choices:
if is_valid(state, choice): // pruning check
apply(state, choice)
BACKTRACK(state, remaining_choices)
undo(state, choice) // backtrack
```
**Key elements**:
1. **State**: The current partial solution being built.
2. **Choices**: The decisions available at each step.
3. **Constraints**: Rules that determine whether a partial solution is valid.
4. **Goal**: The condition that identifies a complete solution.
### Time Complexity
Backtracking explores a search tree. Without pruning, the worst case is O(b^d) where b is the branching factor and d is the depth. Effective pruning can dramatically reduce this in practice.
## Classic Backtracking Problems
### N-Queens
Place n queens on an n x n chessboard so that no two queens threaten each other (no shared row, column, or diagonal).
- **Approach**: Place queens one row at a time. For each row, try each column; prune if the column or either diagonal is already attacked.
- **State**: Positions of queens placed so far.
- **Pruning**: Track occupied columns and diagonals with sets or arrays.
- **Solutions**: 1 for n=1, 0 for n=2 and n=3, 2 for n=4, 10 for n=5, 92 for n=8.
### Sudoku Solving
Fill a 9x9 grid so that each row, column, and 3x3 box contains digits 1-9 exactly once.
- **Approach**: Find the next empty cell, try each digit 1-9, prune if the digit violates row/column/box constraints.
- **State**: The partially filled grid.
- **Pruning**: Check row, column, and box constraints before placing each digit. Advanced: propagate constraints (naked singles, hidden singles) to reduce the search space before branching.
### Subset Sum
Determine whether a subset of a given set sums to a target value.
- **Approach**: For each element, decide to include or exclude it. Prune branches where the running sum already exceeds the target (for positive numbers) or where the remaining elements cannot possibly reach the target.
## Branch and Bound
Branch and bound is an enhancement of backtracking for optimization problems. It maintains a bound (upper or lower, depending on minimization vs maximization) and prunes branches that cannot improve upon the best solution found so far.
### Framework
```
BRANCH_AND_BOUND(state):
if state is a complete solution:
update best_solution if state is better
return
if bound(state) cannot improve on best_solution:
return // prune
for each choice in choices:
apply(state, choice)
BRANCH_AND_BOUND(state)
undo(state, choice)
```
**Key elements**:
1. **Bounding function**: A fast estimate of the best possible solution achievable from the current state.
2. **Pruning**: Skip entire subtrees when the bound proves they cannot contain a better solution.
**Applications**: Traveling Salesman Problem, Integer Linear Programming, Job Scheduling, Knapsack (branch and bound variant).
## Subset Generation
### Iterative (Bitmask) Approach
Generate all 2^n subsets by iterating from 0 to 2^n - 1, where each bit indicates inclusion/exclusion.
```
GENERATE_SUBSETS(S):
n = |S|
for mask = 0 to 2^n - 1:
subset = {S[i] for each bit i set in mask}
process(subset)
```
### Recursive Approach
For each element, recursively generate subsets that include it and subsets that exclude it.
```
SUBSETS(S, index, current):
if index == |S|:
process(current)
return
SUBSETS(S, index + 1, current) // exclude S[index]
SUBSETS(S, index + 1, current + {S[index]}) // include S[index]
```
## Constraint Satisfaction Problems (CSP)
A CSP is defined by:
- **Variables**: The unknowns to be assigned values.
- **Domains**: The possible values for each variable.
- **Constraints**: Rules restricting which combinations of values are valid.
### Solving Strategies
| Strategy | Description |
|----------|-------------|
| **Backtracking search** | Assign variables one at a time, backtrack on constraint violation |
| **Forward checking** | After each assignment, remove inconsistent values from neighboring domains |
| **Arc consistency (AC-3)** | Propagate constraints to prune domains before and during search |
| **Variable ordering (MRV)** | Choose the variable with the Minimum Remaining Values (most constrained) first |
| **Value ordering (LCV)** | Try the Least Constraining Value first (preserves options for other variables) |
**Examples**: Sudoku, map coloring, scheduling, crossword puzzles, register allocation.
## Generating Functions (Conceptual)
Generating functions are a powerful mathematical tool from combinatorics that encode a sequence of numbers as coefficients of a formal power series. While primarily a theoretical tool, they provide closed-form solutions and identities for counting problems.
- **Ordinary generating function** (OGF): A(x) = sum of a_n * x^n. Used for combinations and selections.
- **Exponential generating function** (EGF): A(x) = sum of a_n * x^n / n!. Used for permutations and labeled structures.
**Practical insight**: Even without computing generating functions directly, understanding them helps recognize when a counting problem has a known closed-form solution or recurrence. Knuth uses generating functions extensively in TAOCP to derive exact formulas for combinatorial quantities.
**Example**: The generating function for the number of ways to make change for n cents using coins of denominations d1, d2, ..., dk is the product of 1/(1 - x^di) for each denomination. The coefficient of x^n gives the answer.
## Pruning Strategies
Effective pruning is what makes backtracking practical for large search spaces.
| Strategy | Description | Example |
|----------|-------------|---------|
| **Constraint propagation** | Reduce domains based on current assignments | Sudoku: eliminate placed digits from row/col/box |
| **Bound pruning** | Skip branches that cannot beat the current best | Branch and bound: skip if optimistic bound <= best |
| **Symmetry breaking** | Avoid exploring configurations that are equivalent by symmetry | N-Queens: fix the first queen to the left half |
| **Dominance pruning** | Skip states that are provably worse than another explored state | Knapsack: skip items with worse value-to-weight ratio |
| **Feasibility pruning** | Abandon states that cannot possibly lead to a valid solution | Subset sum: prune if remaining elements cannot reach target |
| **Ordering heuristics** | Process choices in an order likely to find solutions or prune early | MRV for CSPs, try larger values first for optimization |
## Complexity of Combinatorial Problems
| Problem | Brute Force | With Pruning / Optimization |
|---------|-------------|----------------------------|
| All permutations | O(n!) | O(n!) -- must enumerate all |
| All subsets | O(2^n) | O(2^n) -- must enumerate all |
| N-Queens (all solutions) | O(n!) | Significantly less with pruning |
| Sudoku | O(9^81) theoretical | Practical with constraint propagation |
| Subset Sum | O(2^n) | Pseudo-polynomial DP: O(n * target) |
| TSP (exact) | O(n!) | O(n^2 * 2^n) with DP (Held-Karp) |
| Graph Coloring | O(k^n) | NP-complete; effective with backtracking + pruning |
## Knuth's TAOCP Vol. 4A: Key Topics
| Section | Topic |
|---------|-------|
| 7.2.1 | Generating all n-tuples |
| 7.2.1.1 | Generating all permutations |
| 7.2.1.2 | Generating all combinations |
| 7.2.1.3 | Generating all partitions |
| 7.2.1.4 | Generating all set partitions |
| 7.2.1.5 | Generating all trees |
| 7.2.2 | Backtrack programming |
| 7.2.2.1 | Dancing links (Algorithm X) |
**Dancing Links (DLX)**: Knuth's Algorithm X with the "dancing links" technique is an efficient backtracking method for exact cover problems. It represents the constraint matrix as a doubly-linked list structure that allows O(1) removal and restoration of elements, making it highly efficient for problems like Sudoku, pentomino tiling, and N-Queens.
## Best Practices
- Always add pruning to backtracking -- even simple feasibility checks can reduce runtime by orders of magnitude.
- For optimization problems, consider branch and bound before exhaustive enumeration.
- Use constraint propagation (forward checking, arc consistency) for CSPs to reduce the effective search space.
- Choose variable and value ordering heuristics carefully -- MRV and LCV are strong general-purpose strategies.
- Consider whether the problem has symmetries that can be exploited to avoid redundant exploration.
- For problems with overlapping subproblems (e.g., subset sum, TSP), combine backtracking with dynamic programming.
- Reference Knuth's TAOCP Vol. 4A for the most rigorous and comprehensive treatment of combinatorial generation and backtracking, including Algorithm X and dancing links for exact cover problems.
algorithms/combinatorial/metadata.json
{
"version": "1.0.0",
"name": "combinatorial",
"displayName": "Combinatorial Algorithms",
"description": "Use when solving problems involving permutations, combinations, backtracking, branch and bound, subset generation, and constraint satisfaction. Covers N-Queens, Sudoku solving, generating functions, and pruning strategies. Based on Knuth's TAOCP Vol. 4A.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "The Art of Computer Programming, Vol. 4A: Combinatorial Algorithms — Donald Knuth",
"url": "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
},
{
"title": "Combinatorial Optimization — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Combinatorial_optimization"
}
]
}
algorithms/combinatorial/README.md
# Combinatorial Algorithms
Use when solving problems involving permutations, combinations, backtracking, branch and bound, subset generation, and constraint satisfaction. Covers N-Queens, Sudoku solving, generating functions, and pruning strategies. Based on Knuth's TAOCP Vol. 4A.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 7 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/algorithms/combinatorial
```
## License
MIT
algorithms/combinatorial/rules/_sections.md
# Combinatorial Algorithms Rules
Best practices and rules for Combinatorial Algorithms.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Always add pruning to backtracking -- even simple... | CRITICAL | [`combinatorial-always-add-pruning-to-backtracking-even-simple.md`](combinatorial-always-add-pruning-to-backtracking-even-simple.md) |
| 2 | For optimization problems, consider branch and bound before... | LOW | [`combinatorial-for-optimization-problems-consider-branch-and-bound-before.md`](combinatorial-for-optimization-problems-consider-branch-and-bound-before.md) |
| 3 | Use constraint propagation (forward checking, arc... | MEDIUM | [`combinatorial-use-constraint-propagation-forward-checking-arc.md`](combinatorial-use-constraint-propagation-forward-checking-arc.md) |
| 4 | Choose variable and value ordering heuristics carefully --... | MEDIUM | [`combinatorial-choose-variable-and-value-ordering-heuristics-carefully.md`](combinatorial-choose-variable-and-value-ordering-heuristics-carefully.md) |
| 5 | Consider whether the problem has symmetries that can be... | HIGH | [`combinatorial-consider-whether-the-problem-has-symmetries-that-can-be.md`](combinatorial-consider-whether-the-problem-has-symmetries-that-can-be.md) |
| 6 | For problems with overlapping subproblems (e | MEDIUM | [`combinatorial-for-problems-with-overlapping-subproblems-e.md`](combinatorial-for-problems-with-overlapping-subproblems-e.md) |
| 7 | Reference Knuth's TAOCP Vol | MEDIUM | [`combinatorial-reference-knuth-s-taocp-vol.md`](combinatorial-reference-knuth-s-taocp-vol.md) |
algorithms/combinatorial/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: combinatorial, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
algorithms/combinatorial/rules/combinatorial-always-add-pruning-to-backtracking-even-simple.md
---
title: "Always add pruning to backtracking -- even simple..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: combinatorial, dev, algorithms, permutation-and-combination-generation, backtracking-algorithm-design, constraint-satisfaction-problems
---
## Always add pruning to backtracking -- even simple...
Always add pruning to backtracking -- even simple feasibility checks can reduce runtime by orders of magnitude.
algorithms/combinatorial/rules/combinatorial-choose-variable-and-value-ordering-heuristics-carefully.md
---
title: "Choose variable and value ordering heuristics carefully --..."
impact: MEDIUM
impactDescription: "general best practice"
tags: combinatorial, dev, algorithms, permutation-and-combination-generation, backtracking-algorithm-design, constraint-satisfaction-problems
---
## Choose variable and value ordering heuristics carefully --...
Choose variable and value ordering heuristics carefully -- MRV and LCV are strong general-purpose strategies.
algorithms/combinatorial/rules/combinatorial-consider-whether-the-problem-has-symmetries-that-can-be.md
---
title: "Consider whether the problem has symmetries that can be..."
impact: HIGH
impactDescription: "significant quality or reliability improvement"
tags: combinatorial, dev, algorithms, permutation-and-combination-generation, backtracking-algorithm-design, constraint-satisfaction-problems
---
## Consider whether the problem has symmetries that can be...
Consider whether the problem has symmetries that can be exploited to avoid redundant exploration.
algorithms/combinatorial/rules/combinatorial-for-optimization-problems-consider-branch-and-bound-before.md
---
title: "For optimization problems, consider branch and bound before..."
impact: LOW
impactDescription: "recommended but situational"
tags: combinatorial, dev, algorithms, permutation-and-combination-generation, backtracking-algorithm-design, constraint-satisfaction-problems
---
## For optimization problems, consider branch and bound before...
For optimization problems, consider branch and bound before exhaustive enumeration.
algorithms/combinatorial/rules/combinatorial-for-problems-with-overlapping-subproblems-e.md
---
title: "For problems with overlapping subproblems (e"
impact: MEDIUM
impactDescription: "general best practice"
tags: combinatorial, dev, algorithms, permutation-and-combination-generation, backtracking-algorithm-design, constraint-satisfaction-problems
---
## For problems with overlapping subproblems (e
For problems with overlapping subproblems (e.g., subset sum, TSP), combine backtracking with dynamic programming.
algorithms/combinatorial/rules/combinatorial-reference-knuth-s-taocp-vol.md
---
title: "Reference Knuth's TAOCP Vol"
impact: MEDIUM
impactDescription: "general best practice"
tags: combinatorial, dev, algorithms, permutation-and-combination-generation, backtracking-algorithm-design, constraint-satisfaction-problems
---
## Reference Knuth's TAOCP Vol
Reference Knuth's TAOCP Vol. 4A for the most rigorous and comprehensive treatment of combinatorial generation and backtracking, including Algorithm X and dancing links for exact cover problems.
algorithms/combinatorial/rules/combinatorial-use-constraint-propagation-forward-checking-arc.md
---
title: "Use constraint propagation (forward checking, arc..."
impact: MEDIUM
impactDescription: "general best practice"
tags: combinatorial, dev, algorithms, permutation-and-combination-generation, backtracking-algorithm-design, constraint-satisfaction-problems
---
## Use constraint propagation (forward checking, arc...
Use constraint propagation (forward checking, arc consistency) for CSPs to reduce the effective search space.
algorithms/combinatorial/SKILL.md
---
name: combinatorial
description: |
Use when solving problems involving permutations, combinations, backtracking, branch and bound, subset generation, and constraint satisfaction. Covers N-Queens, Sudoku solving, generating functions, and pruning strategies. Based on Knuth's TAOCP Vol. 4A.
USE FOR: permutation and combination generation, backtracking algorithm design, constraint satisfaction problems, branch and bound optimization, subset enumeration, pruning strategy selection
DO NOT USE FOR: graph traversal (use graph-algorithms), optimization with overlapping subproblems (use dynamic-programming)
license: MIT
metadata:
displayName: "Combinatorial Algorithms"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "The Art of Computer Programming, Vol. 4A: Combinatorial Algorithms — Donald Knuth"
url: "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
- title: "Combinatorial Optimization — Wikipedia"
url: "https://en.wikipedia.org/wiki/Combinatorial_optimization"
---
# Combinatorial Algorithms
## Overview
Combinatorial algorithms deal with counting, generating, and optimizing discrete structures -- permutations, combinations, subsets, partitions, and arrangements subject to constraints. Knuth devoted *The Art of Computer Programming, Volume 4A: Combinatorial Algorithms, Part 1* entirely to this topic, calling combinatorics "the mathematics of choice." This skill covers the core techniques for systematic enumeration, backtracking search, and constraint satisfaction.
## Fundamental Counting
### Permutations
An ordered arrangement of n elements. The number of permutations of n distinct elements is n! (n factorial).
- **All permutations of n elements**: n!
- **k-permutations of n elements** (ordered selection of k from n): n! / (n - k)!
- **Permutations with repetition**: n! / (n1! * n2! * ... * nk!) where ni is the count of each repeated element.
### Combinations
An unordered selection of k elements from n. The number of combinations is C(n, k) = n! / (k! * (n - k)!).
- **With repetition** (multiset coefficient): C(n + k - 1, k)
### Subsets
The power set of a set with n elements has 2^n subsets. Each element is either included or excluded.
## Backtracking
Backtracking is a systematic method for generating all (or some) possible configurations of a search space by incrementally building candidates and abandoning a candidate ("backtracking") as soon as it is determined that it cannot lead to a valid solution.
### Backtracking Template
```
BACKTRACK(state, choices):
if state is a complete solution:
process(state)
return
for each choice in choices:
if is_valid(state, choice): // pruning check
apply(state, choice)
BACKTRACK(state, remaining_choices)
undo(state, choice) // backtrack
```
**Key elements**:
1. **State**: The current partial solution being built.
2. **Choices**: The decisions available at each step.
3. **Constraints**: Rules that determine whether a partial solution is valid.
4. **Goal**: The condition that identifies a complete solution.
### Time Complexity
Backtracking explores a search tree. Without pruning, the worst case is O(b^d) where b is the branching factor and d is the depth. Effective pruning can dramatically reduce this in practice.
## Classic Backtracking Problems
### N-Queens
Place n queens on an n x n chessboard so that no two queens threaten each other (no shared row, column, or diagonal).
- **Approach**: Place queens one row at a time. For each row, try each column; prune if the column or either diagonal is already attacked.
- **State**: Positions of queens placed so far.
- **Pruning**: Track occupied columns and diagonals with sets or arrays.
- **Solutions**: 1 for n=1, 0 for n=2 and n=3, 2 for n=4, 10 for n=5, 92 for n=8.
### Sudoku Solving
Fill a 9x9 grid so that each row, column, and 3x3 box contains digits 1-9 exactly once.
- **Approach**: Find the next empty cell, try each digit 1-9, prune if the digit violates row/column/box constraints.
- **State**: The partially filled grid.
- **Pruning**: Check row, column, and box constraints before placing each digit. Advanced: propagate constraints (naked singles, hidden singles) to reduce the search space before branching.
### Subset Sum
Determine whether a subset of a given set sums to a target value.
- **Approach**: For each element, decide to include or exclude it. Prune branches where the running sum already exceeds the target (for positive numbers) or where the remaining elements cannot possibly reach the target.
## Branch and Bound
Branch and bound is an enhancement of backtracking for optimization problems. It maintains a bound (upper or lower, depending on minimization vs maximization) and prunes branches that cannot improve upon the best solution found so far.
### Framework
```
BRANCH_AND_BOUND(state):
if state is a complete solution:
update best_solution if state is better
return
if bound(state) cannot improve on best_solution:
return // prune
for each choice in choices:
apply(state, choice)
BRANCH_AND_BOUND(state)
undo(state, choice)
```
**Key elements**:
1. **Bounding function**: A fast estimate of the best possible solution achievable from the current state.
2. **Pruning**: Skip entire subtrees when the bound proves they cannot contain a better solution.
**Applications**: Traveling Salesman Problem, Integer Linear Programming, Job Scheduling, Knapsack (branch and bound variant).
## Subset Generation
### Iterative (Bitmask) Approach
Generate all 2^n subsets by iterating from 0 to 2^n - 1, where each bit indicates inclusion/exclusion.
```
GENERATE_SUBSETS(S):
n = |S|
for mask = 0 to 2^n - 1:
subset = {S[i] for each bit i set in mask}
process(subset)
```
### Recursive Approach
For each element, recursively generate subsets that include it and subsets that exclude it.
```
SUBSETS(S, index, current):
if index == |S|:
process(current)
return
SUBSETS(S, index + 1, current) // exclude S[index]
SUBSETS(S, index + 1, current + {S[index]}) // include S[index]
```
## Constraint Satisfaction Problems (CSP)
A CSP is defined by:
- **Variables**: The unknowns to be assigned values.
- **Domains**: The possible values for each variable.
- **Constraints**: Rules restricting which combinations of values are valid.
### Solving Strategies
| Strategy | Description |
|----------|-------------|
| **Backtracking search** | Assign variables one at a time, backtrack on constraint violation |
| **Forward checking** | After each assignment, remove inconsistent values from neighboring domains |
| **Arc consistency (AC-3)** | Propagate constraints to prune domains before and during search |
| **Variable ordering (MRV)** | Choose the variable with the Minimum Remaining Values (most constrained) first |
| **Value ordering (LCV)** | Try the Least Constraining Value first (preserves options for other variables) |
**Examples**: Sudoku, map coloring, scheduling, crossword puzzles, register allocation.
## Generating Functions (Conceptual)
Generating functions are a powerful mathematical tool from combinatorics that encode a sequence of numbers as coefficients of a formal power series. While primarily a theoretical tool, they provide closed-form solutions and identities for counting problems.
- **Ordinary generating function** (OGF): A(x) = sum of a_n * x^n. Used for combinations and selections.
- **Exponential generating function** (EGF): A(x) = sum of a_n * x^n / n!. Used for permutations and labeled structures.
**Practical insight**: Even without computing generating functions directly, understanding them helps recognize when a counting problem has a known closed-form solution or recurrence. Knuth uses generating functions extensively in TAOCP to derive exact formulas for combinatorial quantities.
**Example**: The generating function for the number of ways to make change for n cents using coins of denominations d1, d2, ..., dk is the product of 1/(1 - x^di) for each denomination. The coefficient of x^n gives the answer.
## Pruning Strategies
Effective pruning is what makes backtracking practical for large search spaces.
| Strategy | Description | Example |
|----------|-------------|---------|
| **Constraint propagation** | Reduce domains based on current assignments | Sudoku: eliminate placed digits from row/col/box |
| **Bound pruning** | Skip branches that cannot beat the current best | Branch and bound: skip if optimistic bound <= best |
| **Symmetry breaking** | Avoid exploring configurations that are equivalent by symmetry | N-Queens: fix the first queen to the left half |
| **Dominance pruning** | Skip states that are provably worse than another explored state | Knapsack: skip items with worse value-to-weight ratio |
| **Feasibility pruning** | Abandon states that cannot possibly lead to a valid solution | Subset sum: prune if remaining elements cannot reach target |
| **Ordering heuristics** | Process choices in an order likely to find solutions or prune early | MRV for CSPs, try larger values first for optimization |
## Complexity of Combinatorial Problems
| Problem | Brute Force | With Pruning / Optimization |
|---------|-------------|----------------------------|
| All permutations | O(n!) | O(n!) -- must enumerate all |
| All subsets | O(2^n) | O(2^n) -- must enumerate all |
| N-Queens (all solutions) | O(n!) | Significantly less with pruning |
| Sudoku | O(9^81) theoretical | Practical with constraint propagation |
| Subset Sum | O(2^n) | Pseudo-polynomial DP: O(n * target) |
| TSP (exact) | O(n!) | O(n^2 * 2^n) with DP (Held-Karp) |
| Graph Coloring | O(k^n) | NP-complete; effective with backtracking + pruning |
## Knuth's TAOCP Vol. 4A: Key Topics
| Section | Topic |
|---------|-------|
| 7.2.1 | Generating all n-tuples |
| 7.2.1.1 | Generating all permutations |
| 7.2.1.2 | Generating all combinations |
| 7.2.1.3 | Generating all partitions |
| 7.2.1.4 | Generating all set partitions |
| 7.2.1.5 | Generating all trees |
| 7.2.2 | Backtrack programming |
| 7.2.2.1 | Dancing links (Algorithm X) |
**Dancing Links (DLX)**: Knuth's Algorithm X with the "dancing links" technique is an efficient backtracking method for exact cover problems. It represents the constraint matrix as a doubly-linked list structure that allows O(1) removal and restoration of elements, making it highly efficient for problems like Sudoku, pentomino tiling, and N-Queens.
## Best Practices
- Always add pruning to backtracking -- even simple feasibility checks can reduce runtime by orders of magnitude.
- For optimization problems, consider branch and bound before exhaustive enumeration.
- Use constraint propagation (forward checking, arc consistency) for CSPs to reduce the effective search space.
- Choose variable and value ordering heuristics carefully -- MRV and LCV are strong general-purpose strategies.
- Consider whether the problem has symmetries that can be exploited to avoid redundant exploration.
- For problems with overlapping subproblems (e.g., subset sum, TSP), combine backtracking with dynamic programming.
- Reference Knuth's TAOCP Vol. 4A for the most rigorous and comprehensive treatment of combinatorial generation and backtracking, including Algorithm X and dancing links for exact cover problems.
algorithms/data-structures/AGENTS.md
# Data Structures
## Overview
Data structures are the foundation of efficient software. The choice of data structure determines the complexity of every operation your program performs. Knuth's *The Art of Computer Programming, Volume 1: Fundamental Algorithms* provides the definitive treatment of information structures -- from linear lists through trees and multilinked structures. This skill covers the major data structures, their operation complexities, and guidance on when to use each.
## Arrays
A contiguous block of memory storing elements of the same type, accessed by index.
| Operation | Time |
|-----------|------|
| Access by index | O(1) |
| Search (unsorted) | O(n) |
| Search (sorted) | O(log n) |
| Insert at end | O(1) amortized (dynamic array) |
| Insert at position | O(n) |
| Delete at position | O(n) |
**Use when**: Random access is frequent, data size is known or grows by appending, cache locality matters.
## Linked Lists
Elements (nodes) are stored non-contiguously; each node contains data and a pointer to the next (and optionally previous) node.
### Variants
- **Singly linked**: Each node points to the next. Traversal is forward only.
- **Doubly linked**: Each node points to both next and previous. Traversal in both directions.
- **Circular**: The last node points back to the first, forming a cycle.
| Operation | Singly | Doubly |
|-----------|--------|--------|
| Access by index | O(n) | O(n) |
| Search | O(n) | O(n) |
| Insert at head | O(1) | O(1) |
| Insert at tail (with tail pointer) | O(1) | O(1) |
| Insert at position (given pointer) | O(1) | O(1) |
| Delete at head | O(1) | O(1) |
| Delete at position (given pointer) | O(n) for singly, O(1) for doubly | O(1) |
**Use when**: Frequent insertions/deletions at arbitrary positions, no need for random access, implementing stacks/queues.
## Stacks
Last-In, First-Out (LIFO) structure.
| Operation | Time |
|-----------|------|
| Push | O(1) |
| Pop | O(1) |
| Peek/Top | O(1) |
| Search | O(n) |
**Implementations**: Array-based (dynamic array) or linked-list-based.
**Use when**: Undo operations, expression evaluation/parsing, DFS traversal, call stack simulation, balanced parentheses checking.
## Queues
### Standard Queue (FIFO)
First-In, First-Out structure.
| Operation | Time |
|-----------|------|
| Enqueue | O(1) |
| Dequeue | O(1) |
| Peek/Front | O(1) |
| Search | O(n) |
### Deque (Double-Ended Queue)
Insert and remove from both ends.
| Operation | Time |
|-----------|------|
| Insert front/back | O(1) |
| Remove front/back | O(1) |
| Peek front/back | O(1) |
### Priority Queue
Elements are dequeued by priority, not insertion order. Typically implemented with a heap.
| Operation | Time (binary heap) |
|-----------|--------------------|
| Insert | O(log n) |
| Extract-min/max | O(log n) |
| Peek min/max | O(1) |
| Decrease key | O(log n) |
**Use when**: BFS traversal (standard queue), scheduling (priority queue), sliding window problems (deque).
## Hash Tables
Store key-value pairs with near-constant-time access by hashing keys to array indices.
### Collision Handling
- **Chaining**: Each bucket holds a linked list (or other collection) of entries that hash to the same index.
- Simple to implement. Performance degrades to O(n/k) with poor hash distribution.
- **Open Addressing**: All entries stored in the array itself. On collision, probe for the next open slot.
- **Linear probing**: Check the next slot sequentially. Simple but suffers from clustering.
- **Quadratic probing**: Check slots at quadratic intervals. Reduces clustering.
- **Double hashing**: Use a second hash function to determine probe step. Best distribution.
| Operation | Average | Worst |
|-----------|---------|-------|
| Insert | O(1) | O(n) |
| Search | O(1) | O(n) |
| Delete | O(1) | O(n) |
**Load factor**: Ratio of stored elements to table size. Keep below 0.7-0.75 for good performance; resize (rehash) when exceeded.
**Use when**: Fast key-based lookup is critical, keys are hashable, order does not matter.
## Trees
### Binary Search Tree (BST)
Each node has at most two children; left child < parent < right child.
| Operation | Average | Worst (degenerate) |
|-----------|---------|---------------------|
| Search | O(log n) | O(n) |
| Insert | O(log n) | O(n) |
| Delete | O(log n) | O(n) |
### AVL Tree
Self-balancing BST where the height difference between left and right subtrees of any node is at most 1.
| Operation | Time |
|-----------|------|
| Search | O(log n) |
| Insert | O(log n) |
| Delete | O(log n) |
**Tradeoff**: Strictly balanced, so faster lookups than Red-Black trees, but more rotations on insert/delete.
### Red-Black Tree
Self-balancing BST with color properties guaranteeing that the longest path is at most twice the shortest.
| Operation | Time |
|-----------|------|
| Search | O(log n) |
| Insert | O(log n) |
| Delete | O(log n) |
**Tradeoff**: Less strictly balanced than AVL, so fewer rotations on insert/delete but slightly slower lookups. Used in most standard library map/set implementations (C++ std::map, Java TreeMap).
### B-Tree
Generalized self-balancing tree where each node can have many children. Designed for systems that read/write large blocks of data.
| Operation | Time |
|-----------|------|
| Search | O(log n) |
| Insert | O(log n) |
| Delete | O(log n) |
**Use when**: Databases and file systems -- minimizes disk I/O by maximizing keys per node.
### Trie (Prefix Tree)
Tree where each node represents a character; paths from root to leaves represent strings.
| Operation | Time |
|-----------|------|
| Search | O(m) where m = key length |
| Insert | O(m) |
| Delete | O(m) |
| Prefix search | O(m) |
**Use when**: Autocomplete, spell checking, IP routing tables, prefix-based searching.
## Heaps
A complete binary tree satisfying the heap property.
### Min-Heap
Parent <= children. Root is the minimum element.
### Max-Heap
Parent >= children. Root is the maximum element.
| Operation | Time |
|-----------|------|
| Insert | O(log n) |
| Extract min/max | O(log n) |
| Peek min/max | O(1) |
| Build heap | O(n) |
| Decrease/increase key | O(log n) |
**Implementations**: Typically a binary heap backed by an array. For better amortized performance, consider Fibonacci heaps (O(1) amortized insert and decrease-key).
**Use when**: Priority queues, heap sort, finding k-th largest/smallest, median maintenance.
## Graphs
A graph G = (V, E) consists of vertices V and edges E connecting pairs of vertices.
### Representations
#### Adjacency List
Each vertex stores a list of its neighbors.
| Operation | Time |
|-----------|------|
| Add vertex | O(1) |
| Add edge | O(1) |
| Remove edge | O(degree) |
| Check edge exists | O(degree) |
| Space | O(V + E) |
**Best for**: Sparse graphs (E << V^2). Most graph algorithms prefer this representation.
#### Adjacency Matrix
A V x V matrix where entry (i, j) indicates whether an edge exists between vertices i and j.
| Operation | Time |
|-----------|------|
| Add vertex | O(V^2) -- resize |
| Add edge | O(1) |
| Remove edge | O(1) |
| Check edge exists | O(1) |
| Space | O(V^2) |
**Best for**: Dense graphs (E close to V^2), when fast edge existence checks are needed, small graphs.
## Choosing the Right Data Structure
| Need | Data Structure | Why |
|------|---------------|-----|
| Fast index-based access | Array | O(1) random access |
| Fast insertions/deletions anywhere | Linked List | O(1) with pointer |
| LIFO behavior | Stack | Push/pop O(1) |
| FIFO behavior | Queue | Enqueue/dequeue O(1) |
| Fast key-value lookup | Hash Table | O(1) average |
| Ordered key-value storage | BST / Red-Black Tree | O(log n) with ordering |
| Fast lookup with frequent reads | AVL Tree | Strict balance |
| Fast lookup with frequent writes | Red-Black Tree | Fewer rotations |
| Disk-based storage / databases | B-Tree | Minimizes I/O |
| String prefix operations | Trie | O(m) prefix search |
| Priority-based processing | Heap / Priority Queue | O(log n) extract-min/max |
| Modeling relationships | Graph (adjacency list) | Flexible structure |
## Best Practices
- Choose the data structure based on the dominant operation pattern: read-heavy, write-heavy, or balanced.
- Prefer standard library implementations -- they are well-tested and optimized for real-world usage.
- Consider cache locality: arrays and array-backed structures (heaps, hash tables with open addressing) are more cache-friendly than pointer-based structures.
- For concurrent access, consider concurrent variants (ConcurrentHashMap, lock-free queues).
- Remember that theoretical complexity is not the full story -- constant factors, memory allocation patterns, and cache behavior matter in practice.
- Reference Knuth's TAOCP Vol. 1, Chapter 2 (Information Structures) for rigorous treatment of linked structures, trees, and multilinked representations.
algorithms/data-structures/metadata.json
{
"version": "1.0.0",
"name": "data-structures",
"displayName": "Data Structures",
"description": "Use when selecting, implementing, or reasoning about data structures. Covers arrays, linked lists, stacks, queues, hash tables, trees (BST, AVL, Red-Black, B-Tree, Trie), heaps, and graphs (adjacency list, adjacency matrix). Based on Knuth's TAOCP Vol. 1.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "The Art of Computer Programming, Vol. 1: Fundamental Algorithms — Donald Knuth",
"url": "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
},
{
"title": "Data Structure — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Data_structure"
}
]
}
algorithms/data-structures/README.md
# Data Structures
Use when selecting, implementing, or reasoning about data structures. Covers arrays, linked lists, stacks, queues, hash tables, trees (BST, AVL, Red-Black, B-Tree, Trie), heaps, and graphs (adjacency list, adjacency matrix). Based on Knuth's TAOCP Vol. 1.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 6 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/algorithms/data-structures
```
## License
MIT
algorithms/data-structures/rules/_sections.md
# Data Structures Rules
Best practices and rules for Data Structures.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Choose the data structure based on the dominant operation... | MEDIUM | [`data-structures-choose-the-data-structure-based-on-the-dominant-operation.md`](data-structures-choose-the-data-structure-based-on-the-dominant-operation.md) |
| 2 | Prefer standard library implementations -- they are... | LOW | [`data-structures-prefer-standard-library-implementations-they-are.md`](data-structures-prefer-standard-library-implementations-they-are.md) |
| 3 | Consider cache locality | LOW | [`data-structures-consider-cache-locality.md`](data-structures-consider-cache-locality.md) |
| 4 | For concurrent access, consider concurrent variants... | LOW | [`data-structures-for-concurrent-access-consider-concurrent-variants.md`](data-structures-for-concurrent-access-consider-concurrent-variants.md) |
| 5 | Remember that theoretical complexity is not the full story... | MEDIUM | [`data-structures-remember-that-theoretical-complexity-is-not-the-full-story.md`](data-structures-remember-that-theoretical-complexity-is-not-the-full-story.md) |
| 6 | Reference Knuth's TAOCP Vol | MEDIUM | [`data-structures-reference-knuth-s-taocp-vol.md`](data-structures-reference-knuth-s-taocp-vol.md) |
algorithms/data-structures/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: data-structures, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
algorithms/data-structures/rules/data-structures-choose-the-data-structure-based-on-the-dominant-operation.md
---
title: "Choose the data structure based on the dominant operation..."
impact: MEDIUM
impactDescription: "general best practice"
tags: data-structures, dev, algorithms, choosing-data-structures-by-access-pattern, understanding-operation-complexities, implementing-fundamental-data-structures
---
## Choose the data structure based on the dominant operation...
Choose the data structure based on the dominant operation pattern: read-heavy, write-heavy, or balanced.
algorithms/data-structures/rules/data-structures-consider-cache-locality.md
---
title: "Consider cache locality"
impact: LOW
impactDescription: "recommended but situational"
tags: data-structures, dev, algorithms, choosing-data-structures-by-access-pattern, understanding-operation-complexities, implementing-fundamental-data-structures
---
## Consider cache locality
Consider cache locality: arrays and array-backed structures (heaps, hash tables with open addressing) are more cache-friendly than pointer-based structures.
algorithms/data-structures/rules/data-structures-for-concurrent-access-consider-concurrent-variants.md
---
title: "For concurrent access, consider concurrent variants..."
impact: LOW
impactDescription: "recommended but situational"
tags: data-structures, dev, algorithms, choosing-data-structures-by-access-pattern, understanding-operation-complexities, implementing-fundamental-data-structures
---
## For concurrent access, consider concurrent variants...
For concurrent access, consider concurrent variants (ConcurrentHashMap, lock-free queues).
algorithms/data-structures/rules/data-structures-prefer-standard-library-implementations-they-are.md
---
title: "Prefer standard library implementations -- they are..."
impact: LOW
impactDescription: "recommended but situational"
tags: data-structures, dev, algorithms, choosing-data-structures-by-access-pattern, understanding-operation-complexities, implementing-fundamental-data-structures
---
## Prefer standard library implementations -- they are...
Prefer standard library implementations -- they are well-tested and optimized for real-world usage.
algorithms/data-structures/rules/data-structures-reference-knuth-s-taocp-vol.md
---
title: "Reference Knuth's TAOCP Vol"
impact: MEDIUM
impactDescription: "general best practice"
tags: data-structures, dev, algorithms, choosing-data-structures-by-access-pattern, understanding-operation-complexities, implementing-fundamental-data-structures
---
## Reference Knuth's TAOCP Vol
Reference Knuth's TAOCP Vol. 1, Chapter 2 (Information Structures) for rigorous treatment of linked structures, trees, and multilinked representations.
algorithms/data-structures/rules/data-structures-remember-that-theoretical-complexity-is-not-the-full-story.md
---
title: "Remember that theoretical complexity is not the full story..."
impact: MEDIUM
impactDescription: "general best practice"
tags: data-structures, dev, algorithms, choosing-data-structures-by-access-pattern, understanding-operation-complexities, implementing-fundamental-data-structures
---
## Remember that theoretical complexity is not the full story...
Remember that theoretical complexity is not the full story -- constant factors, memory allocation patterns, and cache behavior matter in practice.
algorithms/data-structures/SKILL.md
---
name: data-structures
description: |
Use when selecting, implementing, or reasoning about data structures. Covers arrays, linked lists, stacks, queues, hash tables, trees (BST, AVL, Red-Black, B-Tree, Trie), heaps, and graphs (adjacency list, adjacency matrix). Based on Knuth's TAOCP Vol. 1.
USE FOR: choosing data structures by access pattern, understanding operation complexities, implementing fundamental data structures, comparing data structure tradeoffs
DO NOT USE FOR: graph algorithms on data structures (use graph-algorithms), sorting data (use sorting-searching)
license: MIT
metadata:
displayName: "Data Structures"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "The Art of Computer Programming, Vol. 1: Fundamental Algorithms — Donald Knuth"
url: "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
- title: "Data Structure — Wikipedia"
url: "https://en.wikipedia.org/wiki/Data_structure"
---
# Data Structures
## Overview
Data structures are the foundation of efficient software. The choice of data structure determines the complexity of every operation your program performs. Knuth's *The Art of Computer Programming, Volume 1: Fundamental Algorithms* provides the definitive treatment of information structures -- from linear lists through trees and multilinked structures. This skill covers the major data structures, their operation complexities, and guidance on when to use each.
## Arrays
A contiguous block of memory storing elements of the same type, accessed by index.
| Operation | Time |
|-----------|------|
| Access by index | O(1) |
| Search (unsorted) | O(n) |
| Search (sorted) | O(log n) |
| Insert at end | O(1) amortized (dynamic array) |
| Insert at position | O(n) |
| Delete at position | O(n) |
**Use when**: Random access is frequent, data size is known or grows by appending, cache locality matters.
## Linked Lists
Elements (nodes) are stored non-contiguously; each node contains data and a pointer to the next (and optionally previous) node.
### Variants
- **Singly linked**: Each node points to the next. Traversal is forward only.
- **Doubly linked**: Each node points to both next and previous. Traversal in both directions.
- **Circular**: The last node points back to the first, forming a cycle.
| Operation | Singly | Doubly |
|-----------|--------|--------|
| Access by index | O(n) | O(n) |
| Search | O(n) | O(n) |
| Insert at head | O(1) | O(1) |
| Insert at tail (with tail pointer) | O(1) | O(1) |
| Insert at position (given pointer) | O(1) | O(1) |
| Delete at head | O(1) | O(1) |
| Delete at position (given pointer) | O(n) for singly, O(1) for doubly | O(1) |
**Use when**: Frequent insertions/deletions at arbitrary positions, no need for random access, implementing stacks/queues.
## Stacks
Last-In, First-Out (LIFO) structure.
| Operation | Time |
|-----------|------|
| Push | O(1) |
| Pop | O(1) |
| Peek/Top | O(1) |
| Search | O(n) |
**Implementations**: Array-based (dynamic array) or linked-list-based.
**Use when**: Undo operations, expression evaluation/parsing, DFS traversal, call stack simulation, balanced parentheses checking.
## Queues
### Standard Queue (FIFO)
First-In, First-Out structure.
| Operation | Time |
|-----------|------|
| Enqueue | O(1) |
| Dequeue | O(1) |
| Peek/Front | O(1) |
| Search | O(n) |
### Deque (Double-Ended Queue)
Insert and remove from both ends.
| Operation | Time |
|-----------|------|
| Insert front/back | O(1) |
| Remove front/back | O(1) |
| Peek front/back | O(1) |
### Priority Queue
Elements are dequeued by priority, not insertion order. Typically implemented with a heap.
| Operation | Time (binary heap) |
|-----------|--------------------|
| Insert | O(log n) |
| Extract-min/max | O(log n) |
| Peek min/max | O(1) |
| Decrease key | O(log n) |
**Use when**: BFS traversal (standard queue), scheduling (priority queue), sliding window problems (deque).
## Hash Tables
Store key-value pairs with near-constant-time access by hashing keys to array indices.
### Collision Handling
- **Chaining**: Each bucket holds a linked list (or other collection) of entries that hash to the same index.
- Simple to implement. Performance degrades to O(n/k) with poor hash distribution.
- **Open Addressing**: All entries stored in the array itself. On collision, probe for the next open slot.
- **Linear probing**: Check the next slot sequentially. Simple but suffers from clustering.
- **Quadratic probing**: Check slots at quadratic intervals. Reduces clustering.
- **Double hashing**: Use a second hash function to determine probe step. Best distribution.
| Operation | Average | Worst |
|-----------|---------|-------|
| Insert | O(1) | O(n) |
| Search | O(1) | O(n) |
| Delete | O(1) | O(n) |
**Load factor**: Ratio of stored elements to table size. Keep below 0.7-0.75 for good performance; resize (rehash) when exceeded.
**Use when**: Fast key-based lookup is critical, keys are hashable, order does not matter.
## Trees
### Binary Search Tree (BST)
Each node has at most two children; left child < parent < right child.
| Operation | Average | Worst (degenerate) |
|-----------|---------|---------------------|
| Search | O(log n) | O(n) |
| Insert | O(log n) | O(n) |
| Delete | O(log n) | O(n) |
### AVL Tree
Self-balancing BST where the height difference between left and right subtrees of any node is at most 1.
| Operation | Time |
|-----------|------|
| Search | O(log n) |
| Insert | O(log n) |
| Delete | O(log n) |
**Tradeoff**: Strictly balanced, so faster lookups than Red-Black trees, but more rotations on insert/delete.
### Red-Black Tree
Self-balancing BST with color properties guaranteeing that the longest path is at most twice the shortest.
| Operation | Time |
|-----------|------|
| Search | O(log n) |
| Insert | O(log n) |
| Delete | O(log n) |
**Tradeoff**: Less strictly balanced than AVL, so fewer rotations on insert/delete but slightly slower lookups. Used in most standard library map/set implementations (C++ std::map, Java TreeMap).
### B-Tree
Generalized self-balancing tree where each node can have many children. Designed for systems that read/write large blocks of data.
| Operation | Time |
|-----------|------|
| Search | O(log n) |
| Insert | O(log n) |
| Delete | O(log n) |
**Use when**: Databases and file systems -- minimizes disk I/O by maximizing keys per node.
### Trie (Prefix Tree)
Tree where each node represents a character; paths from root to leaves represent strings.
| Operation | Time |
|-----------|------|
| Search | O(m) where m = key length |
| Insert | O(m) |
| Delete | O(m) |
| Prefix search | O(m) |
**Use when**: Autocomplete, spell checking, IP routing tables, prefix-based searching.
## Heaps
A complete binary tree satisfying the heap property.
### Min-Heap
Parent <= children. Root is the minimum element.
### Max-Heap
Parent >= children. Root is the maximum element.
| Operation | Time |
|-----------|------|
| Insert | O(log n) |
| Extract min/max | O(log n) |
| Peek min/max | O(1) |
| Build heap | O(n) |
| Decrease/increase key | O(log n) |
**Implementations**: Typically a binary heap backed by an array. For better amortized performance, consider Fibonacci heaps (O(1) amortized insert and decrease-key).
**Use when**: Priority queues, heap sort, finding k-th largest/smallest, median maintenance.
## Graphs
A graph G = (V, E) consists of vertices V and edges E connecting pairs of vertices.
### Representations
#### Adjacency List
Each vertex stores a list of its neighbors.
| Operation | Time |
|-----------|------|
| Add vertex | O(1) |
| Add edge | O(1) |
| Remove edge | O(degree) |
| Check edge exists | O(degree) |
| Space | O(V + E) |
**Best for**: Sparse graphs (E << V^2). Most graph algorithms prefer this representation.
#### Adjacency Matrix
A V x V matrix where entry (i, j) indicates whether an edge exists between vertices i and j.
| Operation | Time |
|-----------|------|
| Add vertex | O(V^2) -- resize |
| Add edge | O(1) |
| Remove edge | O(1) |
| Check edge exists | O(1) |
| Space | O(V^2) |
**Best for**: Dense graphs (E close to V^2), when fast edge existence checks are needed, small graphs.
## Choosing the Right Data Structure
| Need | Data Structure | Why |
|------|---------------|-----|
| Fast index-based access | Array | O(1) random access |
| Fast insertions/deletions anywhere | Linked List | O(1) with pointer |
| LIFO behavior | Stack | Push/pop O(1) |
| FIFO behavior | Queue | Enqueue/dequeue O(1) |
| Fast key-value lookup | Hash Table | O(1) average |
| Ordered key-value storage | BST / Red-Black Tree | O(log n) with ordering |
| Fast lookup with frequent reads | AVL Tree | Strict balance |
| Fast lookup with frequent writes | Red-Black Tree | Fewer rotations |
| Disk-based storage / databases | B-Tree | Minimizes I/O |
| String prefix operations | Trie | O(m) prefix search |
| Priority-based processing | Heap / Priority Queue | O(log n) extract-min/max |
| Modeling relationships | Graph (adjacency list) | Flexible structure |
## Best Practices
- Choose the data structure based on the dominant operation pattern: read-heavy, write-heavy, or balanced.
- Prefer standard library implementations -- they are well-tested and optimized for real-world usage.
- Consider cache locality: arrays and array-backed structures (heaps, hash tables with open addressing) are more cache-friendly than pointer-based structures.
- For concurrent access, consider concurrent variants (ConcurrentHashMap, lock-free queues).
- Remember that theoretical complexity is not the full story -- constant factors, memory allocation patterns, and cache behavior matter in practice.
- Reference Knuth's TAOCP Vol. 1, Chapter 2 (Information Structures) for rigorous treatment of linked structures, trees, and multilinked representations.
algorithms/dynamic-programming/AGENTS.md
# Dynamic Programming
## Overview
Dynamic programming (DP) is a method for solving problems by breaking them into overlapping subproblems, solving each subproblem once, and storing the results to avoid redundant computation. Knuth discusses dynamic programming techniques throughout *The Art of Computer Programming*, particularly in the context of optimization, sequence analysis, and combinatorial problems. The term was coined by Richard Bellman in the 1950s.
## Core Principles
### Optimal Substructure
A problem exhibits optimal substructure if an optimal solution to the problem contains optimal solutions to its subproblems. This property allows us to build the global optimum from local optima.
**Example**: The shortest path from A to C through B consists of the shortest path from A to B plus the shortest path from B to C.
### Overlapping Subproblems
A problem has overlapping subproblems when the same subproblems are solved repeatedly in a naive recursive approach. DP eliminates this redundancy by storing results.
**Example**: Computing Fibonacci(n) recursively recomputes Fibonacci(k) for each k < n exponentially many times.
## Two Approaches
### Memoization (Top-Down)
Start with the original problem, recurse into subproblems, and cache results as they are computed.
```
FIB_MEMO(n, cache):
if n <= 1: return n
if n in cache: return cache[n]
cache[n] = FIB_MEMO(n - 1, cache) + FIB_MEMO(n - 2, cache)
return cache[n]
```
**Advantages**: Natural to write (follows recursive structure), computes only the subproblems actually needed.
**Disadvantages**: Recursion overhead, potential stack overflow for deep recursion.
### Tabulation (Bottom-Up)
Build a table from the smallest subproblems up to the desired result, iterating in a careful order.
```
FIB_TABLE(n):
if n <= 1: return n
dp[0] = 0, dp[1] = 1
for i = 2 to n:
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
```
**Advantages**: No recursion overhead, easier to optimize space (often only need the last few entries).
**Disadvantages**: May compute subproblems that are never needed, ordering can be less intuitive.
## The DP Framework
When facing a potential DP problem, follow these steps:
### 1. Define the State
Identify what information is needed to describe a subproblem. This becomes the index/key for your DP table.
**Example** (Knapsack): `dp[i][w]` = maximum value using items 1..i with capacity w.
### 2. Write the Recurrence
Express the solution to a subproblem in terms of smaller subproblems.
**Example** (Knapsack):
```
dp[i][w] = max(
dp[i-1][w], // skip item i
dp[i-1][w - weight[i]] + value[i] // take item i (if weight[i] <= w)
)
```
### 3. Identify the Base Case
Define the values for the smallest subproblems that cannot be decomposed further.
**Example** (Knapsack): `dp[0][w] = 0` for all w (no items means no value).
### 4. Determine the Build Order
For tabulation, compute subproblems in an order such that all dependencies are resolved before they are needed.
**Example** (Knapsack): Process items from i = 1 to n, capacities from w = 0 to W.
### 5. Extract the Answer
The answer to the original problem is at a specific location in the DP table.
**Example** (Knapsack): `dp[n][W]`.
### 6. (Optional) Optimize Space
If the recurrence only depends on the previous row or a fixed number of prior entries, reduce the table accordingly.
**Example** (Fibonacci): Only need dp[i-1] and dp[i-2], so use two variables instead of an array.
## Classic Problems
### Fibonacci Sequence
| Approach | Time | Space |
|----------|------|-------|
| Naive recursion | O(2^n) | O(n) stack |
| Memoization | O(n) | O(n) |
| Tabulation | O(n) | O(n) or O(1) optimized |
### 0/1 Knapsack
Given n items with weights and values, and a knapsack of capacity W, maximize the total value without exceeding the capacity. Each item can be taken at most once.
- **State**: `dp[i][w]` = max value using first i items with capacity w
- **Recurrence**: `dp[i][w] = max(dp[i-1][w], dp[i-1][w-wt[i]] + val[i])`
- **Time**: O(n * W)
- **Space**: O(n * W), or O(W) with rolling array
### Unbounded Knapsack
Same as 0/1 Knapsack, but each item can be taken unlimited times.
- **State**: `dp[w]` = max value with capacity w
- **Recurrence**: `dp[w] = max(dp[w], dp[w-wt[i]] + val[i])` for each item i
- **Time**: O(n * W)
- **Space**: O(W)
### Longest Common Subsequence (LCS)
Find the longest subsequence common to two sequences.
- **State**: `dp[i][j]` = length of LCS of first i characters of X and first j characters of Y
- **Recurrence**:
```
if X[i] == Y[j]: dp[i][j] = dp[i-1][j-1] + 1
else: dp[i][j] = max(dp[i-1][j], dp[i][j-1])
```
- **Time**: O(m * n)
- **Space**: O(m * n), or O(min(m, n)) optimized
### Longest Increasing Subsequence (LIS)
Find the length of the longest strictly increasing subsequence.
- **State**: `dp[i]` = length of LIS ending at index i
- **Recurrence**: `dp[i] = max(dp[j] + 1)` for all j < i where A[j] < A[i]
- **Time**: O(n^2), or O(n log n) with patience sorting (binary search on tails)
- **Space**: O(n)
### Edit Distance (Levenshtein Distance)
Minimum number of operations (insert, delete, replace) to transform one string into another.
- **State**: `dp[i][j]` = edit distance between first i characters of X and first j characters of Y
- **Recurrence**:
```
if X[i] == Y[j]: dp[i][j] = dp[i-1][j-1]
else: dp[i][j] = 1 + min(dp[i-1][j], // delete
dp[i][j-1], // insert
dp[i-1][j-1]) // replace
```
- **Time**: O(m * n)
- **Space**: O(m * n), or O(min(m, n)) optimized
### Coin Change
Given coin denominations and a target amount, find the minimum number of coins needed (or the number of ways to make change).
**Minimum coins:**
- **State**: `dp[a]` = minimum coins to make amount a
- **Recurrence**: `dp[a] = min(dp[a - coin] + 1)` for each coin denomination
- **Base case**: `dp[0] = 0`
- **Time**: O(amount * number_of_coins)
- **Space**: O(amount)
### Matrix Chain Multiplication
Find the optimal way to parenthesize a sequence of matrices to minimize total scalar multiplications.
- **State**: `dp[i][j]` = minimum cost to multiply matrices i through j
- **Recurrence**: `dp[i][j] = min(dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j])` for i <= k < j
- **Base case**: `dp[i][i] = 0`
- **Time**: O(n^3)
- **Space**: O(n^2)
### Rod Cutting
Given a rod of length n and prices for each length, find the maximum revenue from cutting the rod.
- **State**: `dp[l]` = maximum revenue for rod of length l
- **Recurrence**: `dp[l] = max(price[k] + dp[l - k])` for 1 <= k <= l
- **Base case**: `dp[0] = 0`
- **Time**: O(n^2)
- **Space**: O(n)
## DP Problem Complexity Summary
| Problem | Time | Space |
|---------|------|-------|
| Fibonacci | O(n) | O(1) optimized |
| 0/1 Knapsack | O(n * W) | O(W) optimized |
| Unbounded Knapsack | O(n * W) | O(W) |
| LCS | O(m * n) | O(min(m, n)) optimized |
| LIS | O(n log n) | O(n) |
| Edit Distance | O(m * n) | O(min(m, n)) optimized |
| Coin Change | O(amount * coins) | O(amount) |
| Matrix Chain Mult. | O(n^3) | O(n^2) |
| Rod Cutting | O(n^2) | O(n) |
## Memoization vs Tabulation: When to Use Which
| Factor | Memoization (Top-Down) | Tabulation (Bottom-Up) |
|--------|----------------------|----------------------|
| Implementation style | Recursive + cache | Iterative + table |
| Subproblem computation | Only those needed | All subproblems |
| Stack overflow risk | Yes (deep recursion) | No |
| Space optimization | Harder | Easier (rolling arrays) |
| Code clarity | Often more intuitive | Requires careful ordering |
| Performance | Function call overhead | Usually faster in practice |
**Guideline**: Start with memoization for clarity and correctness, then convert to tabulation if performance or space optimization is needed.
## Recognizing DP Problems
A problem is likely solvable with DP if:
1. It asks for an **optimal value** (min, max, count) or the **number of ways** to achieve something.
2. It has **overlapping subproblems** -- naive recursion recomputes the same states.
3. It has **optimal substructure** -- the optimal solution builds on optimal sub-solutions.
4. The problem can be parameterized by a **small set of variables** (the state space is manageable).
## Best Practices
- Always verify optimal substructure before applying DP -- not all optimization problems have it (greedy or exhaustive search may be required instead).
- Define your state precisely and minimally -- extra state dimensions explode the table size.
- Validate your recurrence with small examples before coding.
- Consider whether the problem admits a greedy solution (simpler) before committing to DP.
- For interview/competition settings, practice identifying the state and recurrence quickly -- the implementation follows mechanically.
- Reference Knuth's TAOCP for mathematical rigor on sequence problems, optimal search trees, and combinatorial optimization where DP techniques apply.
algorithms/dynamic-programming/metadata.json
{
"version": "1.0.0",
"name": "dynamic-programming",
"displayName": "Dynamic Programming",
"description": "Use when solving optimization problems with overlapping subproblems and optimal substructure. Covers memoization (top-down) vs tabulation (bottom-up), classic DP problems (Knapsack, LCS, LIS, Edit Distance, Coin Change, Matrix Chain, Rod Cutting), and the DP framework. Based on Knuth's TAOCP.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "The Art of Computer Programming — Donald Knuth",
"url": "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
},
{
"title": "Dynamic Programming — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Dynamic_programming"
}
]
}
algorithms/dynamic-programming/README.md
# Dynamic Programming
Use when solving optimization problems with overlapping subproblems and optimal substructure. Covers memoization (top-down) vs tabulation (bottom-up), classic DP problems (Knapsack, LCS, LIS, Edit Distance, Coin Change, Matrix Chain, Rod Cutting), and the DP framework. Based on Knuth's TAOCP.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 6 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/algorithms/dynamic-programming
```
## License
MIT
algorithms/dynamic-programming/rules/_sections.md
# Dynamic Programming Rules
Best practices and rules for Dynamic Programming.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Always verify optimal substructure before applying DP --... | CRITICAL | [`dynamic-programming-always-verify-optimal-substructure-before-applying-dp.md`](dynamic-programming-always-verify-optimal-substructure-before-applying-dp.md) |
| 2 | Define your state precisely and minimally -- extra state... | MEDIUM | [`dynamic-programming-define-your-state-precisely-and-minimally-extra-state.md`](dynamic-programming-define-your-state-precisely-and-minimally-extra-state.md) |
| 3 | Validate your recurrence with small examples before coding | HIGH | [`dynamic-programming-validate-your-recurrence-with-small-examples-before-coding.md`](dynamic-programming-validate-your-recurrence-with-small-examples-before-coding.md) |
| 4 | Consider whether the problem admits a greedy solution... | LOW | [`dynamic-programming-consider-whether-the-problem-admits-a-greedy-solution.md`](dynamic-programming-consider-whether-the-problem-admits-a-greedy-solution.md) |
| 5 | For interview/competition settings, practice identifying... | MEDIUM | [`dynamic-programming-for-interview-competition-settings-practice-identifying.md`](dynamic-programming-for-interview-competition-settings-practice-identifying.md) |
| 6 | Reference Knuth's TAOCP for mathematical rigor on sequence... | MEDIUM | [`dynamic-programming-reference-knuth-s-taocp-for-mathematical-rigor-on-sequence.md`](dynamic-programming-reference-knuth-s-taocp-for-mathematical-rigor-on-sequence.md) |
algorithms/dynamic-programming/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: dynamic-programming, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
algorithms/dynamic-programming/rules/dynamic-programming-always-verify-optimal-substructure-before-applying-dp.md
---
title: "Always verify optimal substructure before applying DP --..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: dynamic-programming, dev, algorithms, optimization-problems-with-overlapping-subproblems, memoization-strategies, tabulation-approaches
---
## Always verify optimal substructure before applying DP --...
Always verify optimal substructure before applying DP -- not all optimization problems have it (greedy or exhaustive search may be required instead).
algorithms/dynamic-programming/rules/dynamic-programming-consider-whether-the-problem-admits-a-greedy-solution.md
---
title: "Consider whether the problem admits a greedy solution..."
impact: LOW
impactDescription: "recommended but situational"
tags: dynamic-programming, dev, algorithms, optimization-problems-with-overlapping-subproblems, memoization-strategies, tabulation-approaches
---
## Consider whether the problem admits a greedy solution...
Consider whether the problem admits a greedy solution (simpler) before committing to DP.
algorithms/dynamic-programming/rules/dynamic-programming-define-your-state-precisely-and-minimally-extra-state.md
---
title: "Define your state precisely and minimally -- extra state..."
impact: MEDIUM
impactDescription: "general best practice"
tags: dynamic-programming, dev, algorithms, optimization-problems-with-overlapping-subproblems, memoization-strategies, tabulation-approaches
---
## Define your state precisely and minimally -- extra state...
Define your state precisely and minimally -- extra state dimensions explode the table size.
algorithms/dynamic-programming/rules/dynamic-programming-for-interview-competition-settings-practice-identifying.md
---
title: "For interview/competition settings, practice identifying..."
impact: MEDIUM
impactDescription: "general best practice"
tags: dynamic-programming, dev, algorithms, optimization-problems-with-overlapping-subproblems, memoization-strategies, tabulation-approaches
---
## For interview/competition settings, practice identifying...
For interview/competition settings, practice identifying the state and recurrence quickly -- the implementation follows mechanically.
algorithms/dynamic-programming/rules/dynamic-programming-reference-knuth-s-taocp-for-mathematical-rigor-on-sequence.md
---
title: "Reference Knuth's TAOCP for mathematical rigor on sequence..."
impact: MEDIUM
impactDescription: "general best practice"
tags: dynamic-programming, dev, algorithms, optimization-problems-with-overlapping-subproblems, memoization-strategies, tabulation-approaches
---
## Reference Knuth's TAOCP for mathematical rigor on sequence...
Reference Knuth's TAOCP for mathematical rigor on sequence problems, optimal search trees, and combinatorial optimization where DP techniques apply.
algorithms/dynamic-programming/rules/dynamic-programming-validate-your-recurrence-with-small-examples-before-coding.md
---
title: "Validate your recurrence with small examples before coding"
impact: HIGH
impactDescription: "significant quality or reliability improvement"
tags: dynamic-programming, dev, algorithms, optimization-problems-with-overlapping-subproblems, memoization-strategies, tabulation-approaches
---
## Validate your recurrence with small examples before coding
Validate your recurrence with small examples before coding.
algorithms/dynamic-programming/SKILL.md
---
name: dynamic-programming
description: |
Use when solving optimization problems with overlapping subproblems and optimal substructure. Covers memoization (top-down) vs tabulation (bottom-up), classic DP problems (Knapsack, LCS, LIS, Edit Distance, Coin Change, Matrix Chain, Rod Cutting), and the DP framework. Based on Knuth's TAOCP.
USE FOR: optimization problems with overlapping subproblems, memoization strategies, tabulation approaches, recognizing DP problem patterns, state definition and recurrence formulation
DO NOT USE FOR: graph shortest paths (use graph-algorithms), sorting (use sorting-searching)
license: MIT
metadata:
displayName: "Dynamic Programming"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "The Art of Computer Programming — Donald Knuth"
url: "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
- title: "Dynamic Programming — Wikipedia"
url: "https://en.wikipedia.org/wiki/Dynamic_programming"
---
# Dynamic Programming
## Overview
Dynamic programming (DP) is a method for solving problems by breaking them into overlapping subproblems, solving each subproblem once, and storing the results to avoid redundant computation. Knuth discusses dynamic programming techniques throughout *The Art of Computer Programming*, particularly in the context of optimization, sequence analysis, and combinatorial problems. The term was coined by Richard Bellman in the 1950s.
## Core Principles
### Optimal Substructure
A problem exhibits optimal substructure if an optimal solution to the problem contains optimal solutions to its subproblems. This property allows us to build the global optimum from local optima.
**Example**: The shortest path from A to C through B consists of the shortest path from A to B plus the shortest path from B to C.
### Overlapping Subproblems
A problem has overlapping subproblems when the same subproblems are solved repeatedly in a naive recursive approach. DP eliminates this redundancy by storing results.
**Example**: Computing Fibonacci(n) recursively recomputes Fibonacci(k) for each k < n exponentially many times.
## Two Approaches
### Memoization (Top-Down)
Start with the original problem, recurse into subproblems, and cache results as they are computed.
```
FIB_MEMO(n, cache):
if n <= 1: return n
if n in cache: return cache[n]
cache[n] = FIB_MEMO(n - 1, cache) + FIB_MEMO(n - 2, cache)
return cache[n]
```
**Advantages**: Natural to write (follows recursive structure), computes only the subproblems actually needed.
**Disadvantages**: Recursion overhead, potential stack overflow for deep recursion.
### Tabulation (Bottom-Up)
Build a table from the smallest subproblems up to the desired result, iterating in a careful order.
```
FIB_TABLE(n):
if n <= 1: return n
dp[0] = 0, dp[1] = 1
for i = 2 to n:
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
```
**Advantages**: No recursion overhead, easier to optimize space (often only need the last few entries).
**Disadvantages**: May compute subproblems that are never needed, ordering can be less intuitive.
## The DP Framework
When facing a potential DP problem, follow these steps:
### 1. Define the State
Identify what information is needed to describe a subproblem. This becomes the index/key for your DP table.
**Example** (Knapsack): `dp[i][w]` = maximum value using items 1..i with capacity w.
### 2. Write the Recurrence
Express the solution to a subproblem in terms of smaller subproblems.
**Example** (Knapsack):
```
dp[i][w] = max(
dp[i-1][w], // skip item i
dp[i-1][w - weight[i]] + value[i] // take item i (if weight[i] <= w)
)
```
### 3. Identify the Base Case
Define the values for the smallest subproblems that cannot be decomposed further.
**Example** (Knapsack): `dp[0][w] = 0` for all w (no items means no value).
### 4. Determine the Build Order
For tabulation, compute subproblems in an order such that all dependencies are resolved before they are needed.
**Example** (Knapsack): Process items from i = 1 to n, capacities from w = 0 to W.
### 5. Extract the Answer
The answer to the original problem is at a specific location in the DP table.
**Example** (Knapsack): `dp[n][W]`.
### 6. (Optional) Optimize Space
If the recurrence only depends on the previous row or a fixed number of prior entries, reduce the table accordingly.
**Example** (Fibonacci): Only need dp[i-1] and dp[i-2], so use two variables instead of an array.
## Classic Problems
### Fibonacci Sequence
| Approach | Time | Space |
|----------|------|-------|
| Naive recursion | O(2^n) | O(n) stack |
| Memoization | O(n) | O(n) |
| Tabulation | O(n) | O(n) or O(1) optimized |
### 0/1 Knapsack
Given n items with weights and values, and a knapsack of capacity W, maximize the total value without exceeding the capacity. Each item can be taken at most once.
- **State**: `dp[i][w]` = max value using first i items with capacity w
- **Recurrence**: `dp[i][w] = max(dp[i-1][w], dp[i-1][w-wt[i]] + val[i])`
- **Time**: O(n * W)
- **Space**: O(n * W), or O(W) with rolling array
### Unbounded Knapsack
Same as 0/1 Knapsack, but each item can be taken unlimited times.
- **State**: `dp[w]` = max value with capacity w
- **Recurrence**: `dp[w] = max(dp[w], dp[w-wt[i]] + val[i])` for each item i
- **Time**: O(n * W)
- **Space**: O(W)
### Longest Common Subsequence (LCS)
Find the longest subsequence common to two sequences.
- **State**: `dp[i][j]` = length of LCS of first i characters of X and first j characters of Y
- **Recurrence**:
```
if X[i] == Y[j]: dp[i][j] = dp[i-1][j-1] + 1
else: dp[i][j] = max(dp[i-1][j], dp[i][j-1])
```
- **Time**: O(m * n)
- **Space**: O(m * n), or O(min(m, n)) optimized
### Longest Increasing Subsequence (LIS)
Find the length of the longest strictly increasing subsequence.
- **State**: `dp[i]` = length of LIS ending at index i
- **Recurrence**: `dp[i] = max(dp[j] + 1)` for all j < i where A[j] < A[i]
- **Time**: O(n^2), or O(n log n) with patience sorting (binary search on tails)
- **Space**: O(n)
### Edit Distance (Levenshtein Distance)
Minimum number of operations (insert, delete, replace) to transform one string into another.
- **State**: `dp[i][j]` = edit distance between first i characters of X and first j characters of Y
- **Recurrence**:
```
if X[i] == Y[j]: dp[i][j] = dp[i-1][j-1]
else: dp[i][j] = 1 + min(dp[i-1][j], // delete
dp[i][j-1], // insert
dp[i-1][j-1]) // replace
```
- **Time**: O(m * n)
- **Space**: O(m * n), or O(min(m, n)) optimized
### Coin Change
Given coin denominations and a target amount, find the minimum number of coins needed (or the number of ways to make change).
**Minimum coins:**
- **State**: `dp[a]` = minimum coins to make amount a
- **Recurrence**: `dp[a] = min(dp[a - coin] + 1)` for each coin denomination
- **Base case**: `dp[0] = 0`
- **Time**: O(amount * number_of_coins)
- **Space**: O(amount)
### Matrix Chain Multiplication
Find the optimal way to parenthesize a sequence of matrices to minimize total scalar multiplications.
- **State**: `dp[i][j]` = minimum cost to multiply matrices i through j
- **Recurrence**: `dp[i][j] = min(dp[i][k] + dp[k+1][j] + p[i-1]*p[k]*p[j])` for i <= k < j
- **Base case**: `dp[i][i] = 0`
- **Time**: O(n^3)
- **Space**: O(n^2)
### Rod Cutting
Given a rod of length n and prices for each length, find the maximum revenue from cutting the rod.
- **State**: `dp[l]` = maximum revenue for rod of length l
- **Recurrence**: `dp[l] = max(price[k] + dp[l - k])` for 1 <= k <= l
- **Base case**: `dp[0] = 0`
- **Time**: O(n^2)
- **Space**: O(n)
## DP Problem Complexity Summary
| Problem | Time | Space |
|---------|------|-------|
| Fibonacci | O(n) | O(1) optimized |
| 0/1 Knapsack | O(n * W) | O(W) optimized |
| Unbounded Knapsack | O(n * W) | O(W) |
| LCS | O(m * n) | O(min(m, n)) optimized |
| LIS | O(n log n) | O(n) |
| Edit Distance | O(m * n) | O(min(m, n)) optimized |
| Coin Change | O(amount * coins) | O(amount) |
| Matrix Chain Mult. | O(n^3) | O(n^2) |
| Rod Cutting | O(n^2) | O(n) |
## Memoization vs Tabulation: When to Use Which
| Factor | Memoization (Top-Down) | Tabulation (Bottom-Up) |
|--------|----------------------|----------------------|
| Implementation style | Recursive + cache | Iterative + table |
| Subproblem computation | Only those needed | All subproblems |
| Stack overflow risk | Yes (deep recursion) | No |
| Space optimization | Harder | Easier (rolling arrays) |
| Code clarity | Often more intuitive | Requires careful ordering |
| Performance | Function call overhead | Usually faster in practice |
**Guideline**: Start with memoization for clarity and correctness, then convert to tabulation if performance or space optimization is needed.
## Recognizing DP Problems
A problem is likely solvable with DP if:
1. It asks for an **optimal value** (min, max, count) or the **number of ways** to achieve something.
2. It has **overlapping subproblems** -- naive recursion recomputes the same states.
3. It has **optimal substructure** -- the optimal solution builds on optimal sub-solutions.
4. The problem can be parameterized by a **small set of variables** (the state space is manageable).
## Best Practices
- Always verify optimal substructure before applying DP -- not all optimization problems have it (greedy or exhaustive search may be required instead).
- Define your state precisely and minimally -- extra state dimensions explode the table size.
- Validate your recurrence with small examples before coding.
- Consider whether the problem admits a greedy solution (simpler) before committing to DP.
- For interview/competition settings, practice identifying the state and recurrence quickly -- the implementation follows mechanically.
- Reference Knuth's TAOCP for mathematical rigor on sequence problems, optimal search trees, and combinatorial optimization where DP techniques apply.
algorithms/graph-algorithms/AGENTS.md
# Graph Algorithms
## Overview
Graph algorithms solve problems on structures composed of vertices (nodes) and edges (connections). They are central to network analysis, scheduling, routing, social networks, compilers, and countless other domains. Knuth addresses graph algorithms across *The Art of Computer Programming*, particularly in Volumes 1, 4A, and 4B, covering everything from basic traversal to combinatorial graph problems.
## Graph Types
| Type | Description |
|------|-------------|
| **Directed** (digraph) | Edges have direction: (u, v) does not imply (v, u) |
| **Undirected** | Edges are bidirectional: {u, v} connects both ways |
| **Weighted** | Edges carry numeric weights (costs, distances) |
| **Unweighted** | All edges are equivalent (or weight = 1) |
| **Cyclic** | Contains at least one cycle |
| **Acyclic** | Contains no cycles. A directed acyclic graph is a DAG |
| **Connected** | Every vertex is reachable from every other (undirected) |
| **Strongly connected** | Every vertex reachable from every other via directed paths |
## Traversal Algorithms
### Breadth-First Search (BFS)
Explores vertices level by level, visiting all neighbors before moving deeper. Uses a queue.
- **Time**: O(V + E)
- **Space**: O(V)
- **Use for**: Shortest path in unweighted graphs, level-order traversal, checking bipartiteness, finding connected components.
```
BFS(G, source):
create queue Q
mark source as visited
Q.enqueue(source)
while Q is not empty:
u = Q.dequeue()
for each neighbor v of u:
if v is not visited:
mark v as visited
Q.enqueue(v)
```
### Depth-First Search (DFS)
Explores as deep as possible along each branch before backtracking. Uses a stack (or recursion).
- **Time**: O(V + E)
- **Space**: O(V)
- **Use for**: Cycle detection, topological sort, finding connected/strongly connected components, path finding, maze solving.
```
DFS(G, source):
mark source as visited
for each neighbor v of source:
if v is not visited:
DFS(G, v)
```
**Iterative version** (using explicit stack):
```
DFS_ITERATIVE(G, source):
create stack S
S.push(source)
while S is not empty:
u = S.pop()
if u is not visited:
mark u as visited
for each neighbor v of u:
if v is not visited:
S.push(v)
```
## Shortest Path Algorithms
### Dijkstra's Algorithm
Finds the shortest path from a source to all other vertices in a graph with non-negative edge weights.
- **Time**: O((V + E) log V) with a binary heap; O(V^2) with a simple array
- **Space**: O(V)
- **Precondition**: No negative edge weights.
```
DIJKSTRA(G, source):
dist[v] = infinity for all v
dist[source] = 0
create min-priority queue Q with all vertices
while Q is not empty:
u = Q.extract_min()
for each neighbor v of u:
alt = dist[u] + weight(u, v)
if alt < dist[v]:
dist[v] = alt
Q.decrease_key(v, alt)
return dist
```
### Bellman-Ford Algorithm
Finds shortest paths from a source vertex, handling negative edge weights. Can detect negative-weight cycles.
- **Time**: O(V * E)
- **Space**: O(V)
- **Advantage over Dijkstra**: Handles negative weights. Detects negative cycles.
### Floyd-Warshall Algorithm
Finds shortest paths between all pairs of vertices.
- **Time**: O(V^3)
- **Space**: O(V^2)
- **Use for**: Dense graphs where all-pairs shortest paths are needed, detecting negative cycles, transitive closure.
## Minimum Spanning Tree (MST)
A minimum spanning tree connects all vertices with the minimum total edge weight (for undirected, connected, weighted graphs).
### Prim's Algorithm
Grows the MST from a starting vertex, always adding the cheapest edge that connects a new vertex.
- **Time**: O((V + E) log V) with a binary heap; O(V^2) with an adjacency matrix
- **Space**: O(V)
- **Best for**: Dense graphs (adjacency matrix implementation).
### Kruskal's Algorithm
Sorts all edges by weight, then adds edges in order, skipping those that would create a cycle (using Union-Find).
- **Time**: O(E log E) (dominated by sorting)
- **Space**: O(V) for Union-Find
- **Best for**: Sparse graphs, when edges are already sorted or easy to sort.
## Topological Sort
Produces a linear ordering of vertices in a DAG such that for every directed edge (u, v), u comes before v.
- **Algorithms**: Kahn's algorithm (BFS-based, using in-degree tracking) or DFS-based (reverse post-order).
- **Time**: O(V + E)
- **Space**: O(V)
- **Use for**: Build systems, task scheduling, dependency resolution, course prerequisites.
## Strongly Connected Components (SCC)
A strongly connected component is a maximal set of vertices such that there is a directed path from each vertex to every other vertex in the set.
### Tarjan's Algorithm
Single DFS pass using a stack and low-link values.
- **Time**: O(V + E)
- **Space**: O(V)
### Kosaraju's Algorithm
Two DFS passes: first on the original graph (to determine finish order), then on the transposed graph.
- **Time**: O(V + E)
- **Space**: O(V)
**Use for**: Analyzing strongly connected regions in directed graphs, 2-SAT problem solving, condensing a directed graph into its DAG of components.
## Pathfinding
### A* Search
Informed search algorithm that uses a heuristic to guide exploration toward the goal. Combines Dijkstra's actual cost with a heuristic estimate of remaining cost.
- **Time**: Depends on heuristic quality; O(E) in the best case with a perfect heuristic, exponential in the worst case
- **Space**: O(V)
- **Precondition**: Heuristic must be admissible (never overestimates) for optimality. If also consistent (monotone), A* is both optimal and efficient.
- **Use for**: Game pathfinding, map routing, robotics navigation, any problem with a good distance heuristic.
## Union-Find (Disjoint Set Union)
A data structure that tracks elements partitioned into disjoint sets. Supports near-constant-time union and find operations.
| Operation | Time (amortized with path compression + union by rank) |
|-----------|-------------------------------------------------------|
| Find | O(alpha(n)) -- effectively O(1) |
| Union | O(alpha(n)) -- effectively O(1) |
| Space | O(n) |
Where alpha is the inverse Ackermann function, which grows extremely slowly.
**Use for**: Kruskal's MST, detecting cycles in undirected graphs, dynamic connectivity, network connectivity queries.
## Complexity Comparison
| Algorithm | Time | Space | Graph Type |
|-----------|------|-------|------------|
| BFS | O(V + E) | O(V) | Any |
| DFS | O(V + E) | O(V) | Any |
| Dijkstra (binary heap) | O((V + E) log V) | O(V) | Non-negative weights |
| Bellman-Ford | O(V * E) | O(V) | Any (detects negative cycles) |
| Floyd-Warshall | O(V^3) | O(V^2) | All-pairs, any weights |
| Prim (binary heap) | O((V + E) log V) | O(V) | Undirected, weighted |
| Kruskal | O(E log E) | O(V) | Undirected, weighted |
| Topological Sort | O(V + E) | O(V) | DAG |
| Tarjan's SCC | O(V + E) | O(V) | Directed |
| Kosaraju's SCC | O(V + E) | O(V) | Directed |
| A* | O(E) to O(b^d) | O(V) | Weighted, with heuristic |
| Union-Find | O(alpha(n)) per op | O(n) | Disjoint sets |
## Directed vs Undirected: Algorithm Applicability
| Algorithm | Directed | Undirected |
|-----------|----------|------------|
| BFS / DFS | Yes | Yes |
| Dijkstra | Yes | Yes |
| Bellman-Ford | Yes | Yes (treat as bidirectional) |
| Floyd-Warshall | Yes | Yes |
| Topological Sort | Yes (DAG only) | No |
| Tarjan / Kosaraju SCC | Yes | N/A (use connected components) |
| Prim / Kruskal MST | No | Yes |
| A* | Yes | Yes |
## Weighted vs Unweighted: Algorithm Selection
| Scenario | Recommended Algorithm |
|----------|----------------------|
| Shortest path, unweighted | BFS |
| Shortest path, non-negative weights | Dijkstra |
| Shortest path, negative weights possible | Bellman-Ford |
| All-pairs shortest paths | Floyd-Warshall |
| Minimum spanning tree | Prim (dense) or Kruskal (sparse) |
| Reachability / connectivity | BFS or DFS |
## Best Practices
- Always choose the simplest algorithm that handles your constraints: BFS for unweighted shortest paths, Dijkstra for non-negative weights, Bellman-Ford only when negative weights are present.
- For sparse graphs, adjacency list representation is almost always preferred. Use adjacency matrices only for dense graphs or when edge-existence queries dominate.
- When implementing Kruskal's, always use Union-Find with path compression and union by rank for near-constant-time operations.
- For A*, invest time in designing a good heuristic -- the quality of the heuristic determines practical performance.
- Consider whether the graph is a DAG -- many problems simplify dramatically on acyclic graphs (shortest paths become linear time via topological order relaxation).
- Reference Knuth's TAOCP for rigorous mathematical analysis of graph traversal, network flows, and combinatorial graph structures.
algorithms/graph-algorithms/metadata.json
{
"version": "1.0.0",
"name": "graph-algorithms",
"displayName": "Graph Algorithms",
"description": "Use when working with graph problems including traversal, shortest paths, minimum spanning trees, topological sorting, and connectivity analysis. Covers BFS, DFS, Dijkstra, Bellman-Ford, Floyd-Warshall, Prim, Kruskal, Tarjan, Kosaraju, A*, and Union-Find. Based on Knuth's TAOCP.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "The Art of Computer Programming, Vol. 4B: Combinatorial Algorithms — Donald Knuth",
"url": "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
},
{
"title": "Graph Algorithm — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Graph_algorithm"
}
]
}
algorithms/graph-algorithms/README.md
# Graph Algorithms
Use when working with graph problems including traversal, shortest paths, minimum spanning trees, topological sorting, and connectivity analysis. Covers BFS, DFS, Dijkstra, Bellman-Ford, Floyd-Warshall, Prim, Kruskal, Tarjan, Kosaraju, A*, and Union-Find. Based on Knuth's TAOCP.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 6 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/algorithms/graph-algorithms
```
## License
MIT
algorithms/graph-algorithms/rules/_sections.md
# Graph Algorithms Rules
Best practices and rules for Graph Algorithms.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Always choose the simplest algorithm that handles your... | CRITICAL | [`graph-algorithms-always-choose-the-simplest-algorithm-that-handles-your.md`](graph-algorithms-always-choose-the-simplest-algorithm-that-handles-your.md) |
| 2 | For sparse graphs, adjacency list representation is almost... | CRITICAL | [`graph-algorithms-for-sparse-graphs-adjacency-list-representation-is-almost.md`](graph-algorithms-for-sparse-graphs-adjacency-list-representation-is-almost.md) |
| 3 | When implementing Kruskal's, always use Union-Find with... | CRITICAL | [`graph-algorithms-when-implementing-kruskal-s-always-use-union-find-with.md`](graph-algorithms-when-implementing-kruskal-s-always-use-union-find-with.md) |
| 4 | For A*, invest time in designing a good heuristic -- the... | MEDIUM | [`graph-algorithms-for-a-invest-time-in-designing-a-good-heuristic-the.md`](graph-algorithms-for-a-invest-time-in-designing-a-good-heuristic-the.md) |
| 5 | Consider whether the graph is a DAG -- many problems... | LOW | [`graph-algorithms-consider-whether-the-graph-is-a-dag-many-problems.md`](graph-algorithms-consider-whether-the-graph-is-a-dag-many-problems.md) |
| 6 | Reference Knuth's TAOCP for rigorous mathematical analysis... | MEDIUM | [`graph-algorithms-reference-knuth-s-taocp-for-rigorous-mathematical-analysis.md`](graph-algorithms-reference-knuth-s-taocp-for-rigorous-mathematical-analysis.md) |
algorithms/graph-algorithms/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: graph-algorithms, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
algorithms/graph-algorithms/rules/graph-algorithms-always-choose-the-simplest-algorithm-that-handles-your.md
---
title: "Always choose the simplest algorithm that handles your..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: graph-algorithms, dev, algorithms, graph-traversal, shortest-path-computation, minimum-spanning-tree-construction
---
## Always choose the simplest algorithm that handles your...
Always choose the simplest algorithm that handles your constraints: BFS for unweighted shortest paths, Dijkstra for non-negative weights, Bellman-Ford only when negative weights are present.
algorithms/graph-algorithms/rules/graph-algorithms-consider-whether-the-graph-is-a-dag-many-problems.md
---
title: "Consider whether the graph is a DAG -- many problems..."
impact: LOW
impactDescription: "recommended but situational"
tags: graph-algorithms, dev, algorithms, graph-traversal, shortest-path-computation, minimum-spanning-tree-construction
---
## Consider whether the graph is a DAG -- many problems...
Consider whether the graph is a DAG -- many problems simplify dramatically on acyclic graphs (shortest paths become linear time via topological order relaxation).
algorithms/graph-algorithms/rules/graph-algorithms-for-a-invest-time-in-designing-a-good-heuristic-the.md
---
title: "For A*, invest time in designing a good heuristic -- the..."
impact: MEDIUM
impactDescription: "general best practice"
tags: graph-algorithms, dev, algorithms, graph-traversal, shortest-path-computation, minimum-spanning-tree-construction
---
## For A*, invest time in designing a good heuristic -- the...
For A*, invest time in designing a good heuristic -- the quality of the heuristic determines practical performance.
algorithms/graph-algorithms/rules/graph-algorithms-for-sparse-graphs-adjacency-list-representation-is-almost.md
---
title: "For sparse graphs, adjacency list representation is almost..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: graph-algorithms, dev, algorithms, graph-traversal, shortest-path-computation, minimum-spanning-tree-construction
---
## For sparse graphs, adjacency list representation is almost...
For sparse graphs, adjacency list representation is almost always preferred. Use adjacency matrices only for dense graphs or when edge-existence queries dominate.
algorithms/graph-algorithms/rules/graph-algorithms-reference-knuth-s-taocp-for-rigorous-mathematical-analysis.md
---
title: "Reference Knuth's TAOCP for rigorous mathematical analysis..."
impact: MEDIUM
impactDescription: "general best practice"
tags: graph-algorithms, dev, algorithms, graph-traversal, shortest-path-computation, minimum-spanning-tree-construction
---
## Reference Knuth's TAOCP for rigorous mathematical analysis...
Reference Knuth's TAOCP for rigorous mathematical analysis of graph traversal, network flows, and combinatorial graph structures.
algorithms/graph-algorithms/rules/graph-algorithms-when-implementing-kruskal-s-always-use-union-find-with.md
---
title: "When implementing Kruskal's, always use Union-Find with..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: graph-algorithms, dev, algorithms, graph-traversal, shortest-path-computation, minimum-spanning-tree-construction
---
## When implementing Kruskal's, always use Union-Find with...
When implementing Kruskal's, always use Union-Find with path compression and union by rank for near-constant-time operations.
algorithms/graph-algorithms/SKILL.md
---
name: graph-algorithms
description: |
Use when working with graph problems including traversal, shortest paths, minimum spanning trees, topological sorting, and connectivity analysis. Covers BFS, DFS, Dijkstra, Bellman-Ford, Floyd-Warshall, Prim, Kruskal, Tarjan, Kosaraju, A*, and Union-Find. Based on Knuth's TAOCP.
USE FOR: graph traversal, shortest path computation, minimum spanning tree construction, topological sorting, strongly connected components, pathfinding, union-find operations
DO NOT USE FOR: basic data structure operations (use data-structures), optimization problems (use dynamic-programming)
license: MIT
metadata:
displayName: "Graph Algorithms"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "The Art of Computer Programming, Vol. 4B: Combinatorial Algorithms — Donald Knuth"
url: "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
- title: "Graph Algorithm — Wikipedia"
url: "https://en.wikipedia.org/wiki/Graph_algorithm"
---
# Graph Algorithms
## Overview
Graph algorithms solve problems on structures composed of vertices (nodes) and edges (connections). They are central to network analysis, scheduling, routing, social networks, compilers, and countless other domains. Knuth addresses graph algorithms across *The Art of Computer Programming*, particularly in Volumes 1, 4A, and 4B, covering everything from basic traversal to combinatorial graph problems.
## Graph Types
| Type | Description |
|------|-------------|
| **Directed** (digraph) | Edges have direction: (u, v) does not imply (v, u) |
| **Undirected** | Edges are bidirectional: {u, v} connects both ways |
| **Weighted** | Edges carry numeric weights (costs, distances) |
| **Unweighted** | All edges are equivalent (or weight = 1) |
| **Cyclic** | Contains at least one cycle |
| **Acyclic** | Contains no cycles. A directed acyclic graph is a DAG |
| **Connected** | Every vertex is reachable from every other (undirected) |
| **Strongly connected** | Every vertex reachable from every other via directed paths |
## Traversal Algorithms
### Breadth-First Search (BFS)
Explores vertices level by level, visiting all neighbors before moving deeper. Uses a queue.
- **Time**: O(V + E)
- **Space**: O(V)
- **Use for**: Shortest path in unweighted graphs, level-order traversal, checking bipartiteness, finding connected components.
```
BFS(G, source):
create queue Q
mark source as visited
Q.enqueue(source)
while Q is not empty:
u = Q.dequeue()
for each neighbor v of u:
if v is not visited:
mark v as visited
Q.enqueue(v)
```
### Depth-First Search (DFS)
Explores as deep as possible along each branch before backtracking. Uses a stack (or recursion).
- **Time**: O(V + E)
- **Space**: O(V)
- **Use for**: Cycle detection, topological sort, finding connected/strongly connected components, path finding, maze solving.
```
DFS(G, source):
mark source as visited
for each neighbor v of source:
if v is not visited:
DFS(G, v)
```
**Iterative version** (using explicit stack):
```
DFS_ITERATIVE(G, source):
create stack S
S.push(source)
while S is not empty:
u = S.pop()
if u is not visited:
mark u as visited
for each neighbor v of u:
if v is not visited:
S.push(v)
```
## Shortest Path Algorithms
### Dijkstra's Algorithm
Finds the shortest path from a source to all other vertices in a graph with non-negative edge weights.
- **Time**: O((V + E) log V) with a binary heap; O(V^2) with a simple array
- **Space**: O(V)
- **Precondition**: No negative edge weights.
```
DIJKSTRA(G, source):
dist[v] = infinity for all v
dist[source] = 0
create min-priority queue Q with all vertices
while Q is not empty:
u = Q.extract_min()
for each neighbor v of u:
alt = dist[u] + weight(u, v)
if alt < dist[v]:
dist[v] = alt
Q.decrease_key(v, alt)
return dist
```
### Bellman-Ford Algorithm
Finds shortest paths from a source vertex, handling negative edge weights. Can detect negative-weight cycles.
- **Time**: O(V * E)
- **Space**: O(V)
- **Advantage over Dijkstra**: Handles negative weights. Detects negative cycles.
### Floyd-Warshall Algorithm
Finds shortest paths between all pairs of vertices.
- **Time**: O(V^3)
- **Space**: O(V^2)
- **Use for**: Dense graphs where all-pairs shortest paths are needed, detecting negative cycles, transitive closure.
## Minimum Spanning Tree (MST)
A minimum spanning tree connects all vertices with the minimum total edge weight (for undirected, connected, weighted graphs).
### Prim's Algorithm
Grows the MST from a starting vertex, always adding the cheapest edge that connects a new vertex.
- **Time**: O((V + E) log V) with a binary heap; O(V^2) with an adjacency matrix
- **Space**: O(V)
- **Best for**: Dense graphs (adjacency matrix implementation).
### Kruskal's Algorithm
Sorts all edges by weight, then adds edges in order, skipping those that would create a cycle (using Union-Find).
- **Time**: O(E log E) (dominated by sorting)
- **Space**: O(V) for Union-Find
- **Best for**: Sparse graphs, when edges are already sorted or easy to sort.
## Topological Sort
Produces a linear ordering of vertices in a DAG such that for every directed edge (u, v), u comes before v.
- **Algorithms**: Kahn's algorithm (BFS-based, using in-degree tracking) or DFS-based (reverse post-order).
- **Time**: O(V + E)
- **Space**: O(V)
- **Use for**: Build systems, task scheduling, dependency resolution, course prerequisites.
## Strongly Connected Components (SCC)
A strongly connected component is a maximal set of vertices such that there is a directed path from each vertex to every other vertex in the set.
### Tarjan's Algorithm
Single DFS pass using a stack and low-link values.
- **Time**: O(V + E)
- **Space**: O(V)
### Kosaraju's Algorithm
Two DFS passes: first on the original graph (to determine finish order), then on the transposed graph.
- **Time**: O(V + E)
- **Space**: O(V)
**Use for**: Analyzing strongly connected regions in directed graphs, 2-SAT problem solving, condensing a directed graph into its DAG of components.
## Pathfinding
### A* Search
Informed search algorithm that uses a heuristic to guide exploration toward the goal. Combines Dijkstra's actual cost with a heuristic estimate of remaining cost.
- **Time**: Depends on heuristic quality; O(E) in the best case with a perfect heuristic, exponential in the worst case
- **Space**: O(V)
- **Precondition**: Heuristic must be admissible (never overestimates) for optimality. If also consistent (monotone), A* is both optimal and efficient.
- **Use for**: Game pathfinding, map routing, robotics navigation, any problem with a good distance heuristic.
## Union-Find (Disjoint Set Union)
A data structure that tracks elements partitioned into disjoint sets. Supports near-constant-time union and find operations.
| Operation | Time (amortized with path compression + union by rank) |
|-----------|-------------------------------------------------------|
| Find | O(alpha(n)) -- effectively O(1) |
| Union | O(alpha(n)) -- effectively O(1) |
| Space | O(n) |
Where alpha is the inverse Ackermann function, which grows extremely slowly.
**Use for**: Kruskal's MST, detecting cycles in undirected graphs, dynamic connectivity, network connectivity queries.
## Complexity Comparison
| Algorithm | Time | Space | Graph Type |
|-----------|------|-------|------------|
| BFS | O(V + E) | O(V) | Any |
| DFS | O(V + E) | O(V) | Any |
| Dijkstra (binary heap) | O((V + E) log V) | O(V) | Non-negative weights |
| Bellman-Ford | O(V * E) | O(V) | Any (detects negative cycles) |
| Floyd-Warshall | O(V^3) | O(V^2) | All-pairs, any weights |
| Prim (binary heap) | O((V + E) log V) | O(V) | Undirected, weighted |
| Kruskal | O(E log E) | O(V) | Undirected, weighted |
| Topological Sort | O(V + E) | O(V) | DAG |
| Tarjan's SCC | O(V + E) | O(V) | Directed |
| Kosaraju's SCC | O(V + E) | O(V) | Directed |
| A* | O(E) to O(b^d) | O(V) | Weighted, with heuristic |
| Union-Find | O(alpha(n)) per op | O(n) | Disjoint sets |
## Directed vs Undirected: Algorithm Applicability
| Algorithm | Directed | Undirected |
|-----------|----------|------------|
| BFS / DFS | Yes | Yes |
| Dijkstra | Yes | Yes |
| Bellman-Ford | Yes | Yes (treat as bidirectional) |
| Floyd-Warshall | Yes | Yes |
| Topological Sort | Yes (DAG only) | No |
| Tarjan / Kosaraju SCC | Yes | N/A (use connected components) |
| Prim / Kruskal MST | No | Yes |
| A* | Yes | Yes |
## Weighted vs Unweighted: Algorithm Selection
| Scenario | Recommended Algorithm |
|----------|----------------------|
| Shortest path, unweighted | BFS |
| Shortest path, non-negative weights | Dijkstra |
| Shortest path, negative weights possible | Bellman-Ford |
| All-pairs shortest paths | Floyd-Warshall |
| Minimum spanning tree | Prim (dense) or Kruskal (sparse) |
| Reachability / connectivity | BFS or DFS |
## Best Practices
- Always choose the simplest algorithm that handles your constraints: BFS for unweighted shortest paths, Dijkstra for non-negative weights, Bellman-Ford only when negative weights are present.
- For sparse graphs, adjacency list representation is almost always preferred. Use adjacency matrices only for dense graphs or when edge-existence queries dominate.
- When implementing Kruskal's, always use Union-Find with path compression and union by rank for near-constant-time operations.
- For A*, invest time in designing a good heuristic -- the quality of the heuristic determines practical performance.
- Consider whether the graph is a DAG -- many problems simplify dramatically on acyclic graphs (shortest paths become linear time via topological order relaxation).
- Reference Knuth's TAOCP for rigorous mathematical analysis of graph traversal, network flows, and combinatorial graph structures.
algorithms/metadata.json
{
"version": "1.0.0",
"name": "algorithms",
"displayName": "Algorithms & Data Structures",
"description": "Use when selecting algorithms, analyzing complexity, or reasoning about data structure choices. Covers Big-O notation, space vs time tradeoffs, amortized analysis, and algorithmic problem-solving strategy based on Knuth's \"The Art of Computer Programming.\"",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "The Art of Computer Programming — Donald Knuth",
"url": "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
},
{
"title": "Algorithm — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Algorithm"
}
]
}
algorithms/README.md
# Algorithms & Data Structures
Use when selecting algorithms, analyzing complexity, or reasoning about data structure choices. Covers Big-O notation, space vs time tradeoffs, amortized analysis, and algorithmic problem-solving strategy based on Knuth's "The Art of Computer Programming."
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 5 individual best practice rules |
## Sub-skills
| Skill | Description |
|-------|-------------|
| [`combinatorial/`](combinatorial/) | Use when solving problems involving permutations, combinations, backtracking, branch and bound, subset generation, and c... |
| [`data-structures/`](data-structures/) | Use when selecting, implementing, or reasoning about data structures. Covers arrays, linked lists, stacks, queues, hash ... |
| [`dynamic-programming/`](dynamic-programming/) | Use when solving optimization problems with overlapping subproblems and optimal substructure. Covers memoization (top-do... |
| [`graph-algorithms/`](graph-algorithms/) | Use when working with graph problems including traversal, shortest paths, minimum spanning trees, topological sorting, a... |
| [`sorting-searching/`](sorting-searching/) | Use when implementing or selecting sorting and searching algorithms. Covers comparison sorts (Quicksort, Mergesort, Heap... |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/algorithms
```
## License
MIT
algorithms/rules/_sections.md
# Algorithms & Data Structures Rules
Best practices and rules for Algorithms & Data Structures.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Start with the simplest correct algorithm, then optimize if... | MEDIUM | [`algorithms-start-with-the-simplest-correct-algorithm-then-optimize-if.md`](algorithms-start-with-the-simplest-correct-algorithm-then-optimize-if.md) |
| 2 | Know the standard library -- most languages provide... | MEDIUM | [`algorithms-know-the-standard-library-most-languages-provide.md`](algorithms-know-the-standard-library-most-languages-provide.md) |
| 3 | Prefer algorithms with good average-case behavior for... | CRITICAL | [`algorithms-prefer-algorithms-with-good-average-case-behavior-for.md`](algorithms-prefer-algorithms-with-good-average-case-behavior-for.md) |
| 4 | Understand amortized costs before concluding that an... | MEDIUM | [`algorithms-understand-amortized-costs-before-concluding-that-an.md`](algorithms-understand-amortized-costs-before-concluding-that-an.md) |
| 5 | Reference Knuth's TAOCP for rigorous analysis and... | MEDIUM | [`algorithms-reference-knuth-s-taocp-for-rigorous-analysis-and.md`](algorithms-reference-knuth-s-taocp-for-rigorous-analysis-and.md) |
algorithms/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: algorithms, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
algorithms/rules/algorithms-know-the-standard-library-most-languages-provide.md
---
title: "Know the standard library -- most languages provide..."
impact: MEDIUM
impactDescription: "general best practice"
tags: algorithms, dev, algorithm-selection, big-o-analysis, complexity-comparison
---
## Know the standard library -- most languages provide...
Know the standard library -- most languages provide well-optimized sorting, searching, and data structure implementations.
algorithms/rules/algorithms-prefer-algorithms-with-good-average-case-behavior-for.md
---
title: "Prefer algorithms with good average-case behavior for..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: algorithms, dev, algorithm-selection, big-o-analysis, complexity-comparison
---
## Prefer algorithms with good average-case behavior for...
Prefer algorithms with good average-case behavior for general use; consider worst-case guarantees for safety-critical systems.
algorithms/rules/algorithms-reference-knuth-s-taocp-for-rigorous-analysis-and.md
---
title: "Reference Knuth's TAOCP for rigorous analysis and..."
impact: MEDIUM
impactDescription: "general best practice"
tags: algorithms, dev, algorithm-selection, big-o-analysis, complexity-comparison
---
## Reference Knuth's TAOCP for rigorous analysis and...
Reference Knuth's TAOCP for rigorous analysis and historical context on any fundamental algorithm.
algorithms/rules/algorithms-start-with-the-simplest-correct-algorithm-then-optimize-if.md
---
title: "Start with the simplest correct algorithm, then optimize if..."
impact: MEDIUM
impactDescription: "general best practice"
tags: algorithms, dev, algorithm-selection, big-o-analysis, complexity-comparison
---
## Start with the simplest correct algorithm, then optimize if...
Start with the simplest correct algorithm, then optimize if profiling shows a bottleneck.
algorithms/rules/algorithms-understand-amortized-costs-before-concluding-that-an.md
---
title: "Understand amortized costs before concluding that an..."
impact: MEDIUM
impactDescription: "general best practice"
tags: algorithms, dev, algorithm-selection, big-o-analysis, complexity-comparison
---
## Understand amortized costs before concluding that an...
Understand amortized costs before concluding that an operation is "slow" based on a single invocation.
algorithms/SKILL.md
---
name: algorithms
description: |
Use when selecting algorithms, analyzing complexity, or reasoning about data structure choices. Covers Big-O notation, space vs time tradeoffs, amortized analysis, and algorithmic problem-solving strategy based on Knuth's "The Art of Computer Programming."
USE FOR: algorithm selection, Big-O analysis, complexity comparison, choosing data structures, algorithmic problem-solving strategy
DO NOT USE FOR: specific algorithm implementations (use sub-skills), system architecture (use dev/architecture), design patterns (use dev/design-patterns)
license: MIT
metadata:
displayName: "Algorithms & Data Structures"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "The Art of Computer Programming — Donald Knuth"
url: "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
- title: "Algorithm — Wikipedia"
url: "https://en.wikipedia.org/wiki/Algorithm"
---
# Algorithms & Data Structures
## Overview
This skill covers the foundational principles of algorithmic thinking, drawn primarily from Donald Knuth's *The Art of Computer Programming* (TAOCP). It provides guidance on analyzing algorithm efficiency, understanding complexity classes, and choosing the right algorithmic approach for a given problem.
## Canonical Reference
| Volume | Title | Covers |
|--------|-------|--------|
| TAOCP Vol. 1 | *Fundamental Algorithms* | Data structures, mathematical foundations, information structures |
| TAOCP Vol. 2 | *Seminumerical Algorithms* | Random numbers, arithmetic, floating-point |
| TAOCP Vol. 3 | *Sorting and Searching* | Sorting, searching, comparison of methods |
| TAOCP Vol. 4A | *Combinatorial Algorithms, Part 1* | Combinatorial generation, backtracking, constraint satisfaction |
| TAOCP Vol. 4B | *Combinatorial Algorithms, Part 2* | Satisfiability, graph algorithms |
## Big-O Notation
Big-O describes the upper bound of an algorithm's growth rate as input size increases. It characterizes the worst-case behavior and allows comparison between algorithms independent of hardware.
### Common Complexity Classes
| Notation | Name | Example |
|----------|------|---------|
| O(1) | Constant | Hash table lookup, array index access |
| O(log n) | Logarithmic | Binary search, balanced BST lookup |
| O(n) | Linear | Linear search, single array traversal |
| O(n log n) | Linearithmic | Mergesort, Heapsort, efficient comparison sorts |
| O(n^2) | Quadratic | Bubble sort, insertion sort (worst case), nested loops |
| O(2^n) | Exponential | Recursive Fibonacci (naive), subset enumeration |
### Growth Rate Comparison
```
n O(1) O(log n) O(n) O(n log n) O(n^2) O(2^n)
1 1 0 1 0 1 2
10 1 3.3 10 33 100 1,024
100 1 6.6 100 664 10,000 ~1.27 x 10^30
1,000 1 10 1,000 10,000 1,000,000 ~1.07 x 10^301
10,000 1 13.3 10,000 133,000 100,000,000 (infeasible)
```
## Space vs Time Tradeoffs
Every algorithm makes a tradeoff between how much memory it uses and how fast it runs. Key principles:
- **Caching / Memoization**: Use extra space to store computed results and avoid redundant work (trades space for time).
- **In-place algorithms**: Minimize space usage at the cost of potentially more complex logic or slower execution (trades time for space).
- **Lookup tables**: Precompute results and store them for O(1) access (trades space for time).
- **Compression**: Reduce space at the cost of encoding/decoding time (trades time for space).
| Strategy | Space | Time | Example |
|----------|-------|------|---------|
| Memoized recursion | O(n) extra | Avoids recomputation | DP Fibonacci |
| In-place sort | O(1) extra | May be slower | Heapsort vs Mergesort |
| Hash table | O(n) extra | O(1) average lookup | Two-sum problem |
| Bit manipulation | O(1) extra | Constant factor overhead | Flags, compact sets |
## Amortized Analysis
Amortized analysis averages the cost of operations over a sequence, even when individual operations may be expensive. It provides a tighter bound than worst-case analysis for data structures that occasionally restructure.
- **Aggregate method**: Total cost of n operations divided by n.
- **Accounting method**: Assign different charges to different operations; overcharges on cheap operations "pay" for expensive ones.
- **Potential method**: Define a potential function on the data structure state; amortized cost = actual cost + change in potential.
**Example**: Dynamic array (ArrayList) doubling. Individual insertions are O(1) amortized even though resizing copies all elements, because resizing happens infrequently (the cost of copying is spread across the insertions that preceded it).
## Choosing Algorithms by Problem Type
| Problem Type | Recommended Approach | Sub-Skill |
|--------------|---------------------|-----------|
| Ordering elements | Comparison sort (Quicksort, Mergesort) or linear sort (Radix) | sorting-searching |
| Finding elements | Binary search, hash-based lookup | sorting-searching |
| Storing/retrieving structured data | Choose appropriate data structure by access pattern | data-structures |
| Shortest path / connectivity | Graph algorithms (BFS, DFS, Dijkstra) | graph-algorithms |
| Optimization with overlapping subproblems | Dynamic programming | dynamic-programming |
| Enumerating configurations / constraint solving | Backtracking, branch and bound | combinatorial |
| String matching | KMP, Rabin-Karp, suffix structures | sorting-searching |
| Scheduling / ordering dependencies | Topological sort | graph-algorithms |
| Minimum spanning tree | Prim's, Kruskal's | graph-algorithms |
| Subset/permutation generation | Combinatorial generation | combinatorial |
## Algorithm Analysis Checklist
When evaluating or selecting an algorithm:
1. **Identify the problem class** -- Is it a searching, sorting, graph, optimization, or enumeration problem?
2. **Determine input constraints** -- What is the expected input size? Are there special properties (sorted, sparse, bounded range)?
3. **Analyze time complexity** -- What is the worst-case, average-case, and best-case behavior?
4. **Analyze space complexity** -- How much auxiliary memory is required?
5. **Consider stability and determinism** -- Does order preservation matter? Is randomness acceptable?
6. **Evaluate practical constants** -- Two O(n log n) algorithms may differ significantly in constant factors and cache behavior.
7. **Benchmark with real data** -- Asymptotic analysis is a starting point; real-world performance depends on data distribution and hardware.
## Best Practices
- Start with the simplest correct algorithm, then optimize if profiling shows a bottleneck.
- Know the standard library -- most languages provide well-optimized sorting, searching, and data structure implementations.
- Prefer algorithms with good average-case behavior for general use; consider worst-case guarantees for safety-critical systems.
- Understand amortized costs before concluding that an operation is "slow" based on a single invocation.
- Reference Knuth's TAOCP for rigorous analysis and historical context on any fundamental algorithm.
algorithms/sorting-searching/AGENTS.md
# Sorting & Searching Algorithms
## Overview
Sorting and searching are among the most fundamental operations in computer science. Knuth dedicated the entirety of *The Art of Computer Programming, Volume 3: Sorting and Searching* to these topics, reflecting their importance and depth. This skill covers the major algorithms in both categories, their complexities, and guidance on when to use each.
## Sorting Algorithms
### Comparison Sorts
Comparison-based sorts determine order by comparing pairs of elements. The theoretical lower bound for comparison sorting is O(n log n).
#### Quicksort
- **Strategy**: Divide and conquer. Pick a pivot, partition the array so elements less than the pivot come before it and elements greater come after, then recurse.
- **Time**: O(n log n) average, O(n^2) worst case (poor pivot choice)
- **Space**: O(log n) stack space (in-place partitioning)
- **Stable**: No
- **Best for**: General-purpose sorting; fastest in practice for most data distributions due to excellent cache behavior.
```
QUICKSORT(A, lo, hi):
if lo < hi:
p = PARTITION(A, lo, hi)
QUICKSORT(A, lo, p - 1)
QUICKSORT(A, p + 1, hi)
PARTITION(A, lo, hi):
pivot = A[hi]
i = lo - 1
for j = lo to hi - 1:
if A[j] <= pivot:
i = i + 1
swap A[i] and A[j]
swap A[i + 1] and A[hi]
return i + 1
```
#### Mergesort
- **Strategy**: Divide and conquer. Split the array in half, recursively sort each half, then merge the two sorted halves.
- **Time**: O(n log n) in all cases
- **Space**: O(n) auxiliary
- **Stable**: Yes
- **Best for**: When stability is required, linked lists, external sorting (large files that do not fit in memory).
```
MERGESORT(A, lo, hi):
if lo < hi:
mid = (lo + hi) / 2
MERGESORT(A, lo, mid)
MERGESORT(A, mid + 1, hi)
MERGE(A, lo, mid, hi)
MERGE(A, lo, mid, hi):
create temporary arrays L = A[lo..mid], R = A[mid+1..hi]
i = 0, j = 0, k = lo
while i < |L| and j < |R|:
if L[i] <= R[j]:
A[k] = L[i]; i++
else:
A[k] = R[j]; j++
k++
copy remaining elements of L or R into A
```
#### Heapsort
- **Strategy**: Build a max-heap from the array, then repeatedly extract the maximum to build the sorted result.
- **Time**: O(n log n) in all cases
- **Space**: O(1) (in-place)
- **Stable**: No
- **Best for**: When guaranteed O(n log n) is needed without extra memory. Not cache-friendly, so often slower than Quicksort in practice.
#### Insertion Sort
- **Strategy**: Build the sorted array one element at a time by inserting each element into its correct position among the already-sorted elements.
- **Time**: O(n) best case (nearly sorted), O(n^2) worst/average case
- **Space**: O(1) (in-place)
- **Stable**: Yes
- **Best for**: Small arrays, nearly sorted data, online sorting (data arrives one element at a time). Often used as the base case in hybrid sorts.
#### Timsort
- **Strategy**: Hybrid of Mergesort and Insertion sort. Identifies natural runs (already sorted subsequences), extends them with insertion sort, then merges runs using an optimized merge procedure.
- **Time**: O(n) best case (already sorted), O(n log n) worst case
- **Space**: O(n) auxiliary
- **Stable**: Yes
- **Best for**: Real-world data that often contains partially sorted subsequences. Default sort in Python and Java.
### Linear Sorts
These sorts exploit constraints on the input (bounded range, integer keys) to beat the O(n log n) comparison lower bound.
#### Counting Sort
- **Strategy**: Count occurrences of each value, then compute positions from cumulative counts.
- **Time**: O(n + k) where k is the range of input values
- **Space**: O(n + k)
- **Stable**: Yes
- **Best for**: Small integer ranges (e.g., sorting grades 0-100, characters).
#### Radix Sort
- **Strategy**: Sort by each digit/character position from least significant to most significant, using a stable sub-sort (typically counting sort) at each position.
- **Time**: O(d * (n + k)) where d is the number of digits and k is the radix
- **Space**: O(n + k)
- **Stable**: Yes
- **Best for**: Fixed-length integers or strings with bounded alphabet.
#### Bucket Sort
- **Strategy**: Distribute elements into buckets based on value range, sort each bucket individually, then concatenate.
- **Time**: O(n + k) average when input is uniformly distributed; O(n^2) worst case
- **Space**: O(n + k)
- **Stable**: Depends on bucket sort
- **Best for**: Uniformly distributed floating-point numbers in a known range.
### Sorting Complexity Comparison
| Algorithm | Best | Average | Worst | Space | Stable |
|-----------|------|---------|-------|-------|--------|
| Quicksort | O(n log n) | O(n log n) | O(n^2) | O(log n) | No |
| Mergesort | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes |
| Heapsort | O(n log n) | O(n log n) | O(n log n) | O(1) | No |
| Insertion Sort | O(n) | O(n^2) | O(n^2) | O(1) | Yes |
| Timsort | O(n) | O(n log n) | O(n log n) | O(n) | Yes |
| Counting Sort | O(n + k) | O(n + k) | O(n + k) | O(n + k) | Yes |
| Radix Sort | O(d(n + k)) | O(d(n + k)) | O(d(n + k)) | O(n + k) | Yes |
| Bucket Sort | O(n + k) | O(n + k) | O(n^2) | O(n + k) | Varies |
### When to Use Which Sort
| Situation | Recommended Sort |
|-----------|-----------------|
| General purpose, in-memory | Quicksort (or language default, often Timsort) |
| Stability required | Mergesort or Timsort |
| Guaranteed O(n log n), no extra space | Heapsort |
| Small arrays (n < 20) | Insertion Sort |
| Nearly sorted data | Insertion Sort or Timsort |
| Integer keys with small range | Counting Sort |
| Fixed-length integer/string keys | Radix Sort |
| Uniformly distributed floating-point data | Bucket Sort |
| External sorting (data on disk) | External Mergesort |
| Linked list sorting | Mergesort |
## Searching Algorithms
### Binary Search
- **Precondition**: Array must be sorted.
- **Strategy**: Repeatedly halve the search space by comparing the target to the middle element.
- **Time**: O(log n)
- **Space**: O(1) iterative, O(log n) recursive
```
BINARY_SEARCH(A, target):
lo = 0, hi = len(A) - 1
while lo <= hi:
mid = lo + (hi - lo) / 2
if A[mid] == target:
return mid
else if A[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1 // not found
```
**Variations**: Lower bound (first occurrence), upper bound (last occurrence), search on answer (binary search the solution space).
### Interpolation Search
- **Precondition**: Array must be sorted and values uniformly distributed.
- **Strategy**: Estimate the position of the target based on its value relative to the min and max of the current range.
- **Time**: O(log log n) average for uniformly distributed data, O(n) worst case
- **Best for**: Large, uniformly distributed sorted datasets.
### Two Pointers Technique
- **Strategy**: Use two pointers (indices) that move through the data structure, typically from opposite ends or at different speeds.
- **Time**: Typically O(n)
- **Common applications**: Pair sum in sorted array, removing duplicates, partitioning, palindrome check, merging sorted arrays.
### Sliding Window Technique
- **Strategy**: Maintain a window (contiguous subarray) that slides through the array, expanding or shrinking to satisfy a condition.
- **Time**: Typically O(n)
- **Common applications**: Maximum/minimum subarray of size k, longest substring without repeating characters, smallest subarray with sum >= target.
### Searching Complexity Comparison
| Algorithm | Average | Worst | Precondition |
|-----------|---------|-------|--------------|
| Linear Search | O(n) | O(n) | None |
| Binary Search | O(log n) | O(log n) | Sorted |
| Interpolation Search | O(log log n) | O(n) | Sorted, uniform distribution |
| Hash Lookup | O(1) | O(n) | Hash table built |
| Two Pointers | O(n) | O(n) | Often sorted |
| Sliding Window | O(n) | O(n) | Contiguous subarray problems |
## Stability in Sorting
A sort is **stable** if elements with equal keys retain their original relative order after sorting. Stability matters when:
- Sorting by multiple keys (sort by secondary key first, then by primary key with a stable sort).
- Preserving meaningful insertion order.
- Composing sorts for multi-level ordering.
## Best Practices
- Use your language's built-in sort (typically Timsort or Introsort) unless you have a specific reason not to -- they are highly optimized.
- For searching in a sorted collection, always prefer binary search over linear search.
- Consider the two pointers technique before reaching for nested loops on sorted data.
- The sliding window technique converts many O(n^2) brute-force subarray problems into O(n).
- When data has bounded integer keys, consider counting or radix sort for linear-time performance.
- Reference Knuth's TAOCP Vol. 3 for rigorous analysis of any sorting or searching method.
algorithms/sorting-searching/metadata.json
{
"version": "1.0.0",
"name": "sorting-searching",
"displayName": "Sorting & Searching Algorithms",
"description": "Use when implementing or selecting sorting and searching algorithms. Covers comparison sorts (Quicksort, Mergesort, Heapsort, Insertion sort, Timsort), linear sorts (Counting, Radix, Bucket), and searching techniques (binary search, interpolation search, two pointers, sliding window). Based on Knuth's TAOCP Vol. 3.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "The Art of Computer Programming, Vol. 3: Sorting and Searching — Donald Knuth",
"url": "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
},
{
"title": "Sorting Algorithm — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Sorting_algorithm"
}
]
}
algorithms/sorting-searching/README.md
# Sorting & Searching Algorithms
Use when implementing or selecting sorting and searching algorithms. Covers comparison sorts (Quicksort, Mergesort, Heapsort, Insertion sort, Timsort), linear sorts (Counting, Radix, Bucket), and searching techniques (binary search, interpolation search, two pointers, sliding window). Based on Knuth's TAOCP Vol. 3.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 6 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/algorithms/sorting-searching
```
## License
MIT
algorithms/sorting-searching/rules/_sections.md
# Sorting & Searching Algorithms Rules
Best practices and rules for Sorting & Searching Algorithms.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Use your language's built-in sort (typically Timsort or... | MEDIUM | [`sorting-searching-use-your-language-s-built-in-sort-typically-timsort-or.md`](sorting-searching-use-your-language-s-built-in-sort-typically-timsort-or.md) |
| 2 | For searching in a sorted collection, always prefer binary... | CRITICAL | [`sorting-searching-for-searching-in-a-sorted-collection-always-prefer-binary.md`](sorting-searching-for-searching-in-a-sorted-collection-always-prefer-binary.md) |
| 3 | Consider the two pointers technique before reaching for... | LOW | [`sorting-searching-consider-the-two-pointers-technique-before-reaching-for.md`](sorting-searching-consider-the-two-pointers-technique-before-reaching-for.md) |
| 4 | The sliding window technique converts many O(n^2)... | MEDIUM | [`sorting-searching-the-sliding-window-technique-converts-many-o-n-2.md`](sorting-searching-the-sliding-window-technique-converts-many-o-n-2.md) |
| 5 | When data has bounded integer keys, consider counting or... | LOW | [`sorting-searching-when-data-has-bounded-integer-keys-consider-counting-or.md`](sorting-searching-when-data-has-bounded-integer-keys-consider-counting-or.md) |
| 6 | Reference Knuth's TAOCP Vol | MEDIUM | [`sorting-searching-reference-knuth-s-taocp-vol.md`](sorting-searching-reference-knuth-s-taocp-vol.md) |
algorithms/sorting-searching/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: sorting-searching, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
algorithms/sorting-searching/rules/sorting-searching-consider-the-two-pointers-technique-before-reaching-for.md
---
title: "Consider the two pointers technique before reaching for..."
impact: LOW
impactDescription: "recommended but situational"
tags: sorting-searching, dev, algorithms, sorting-algorithm-selection, searching-algorithm-selection, understanding-sort-stability
---
## Consider the two pointers technique before reaching for...
Consider the two pointers technique before reaching for nested loops on sorted data.
algorithms/sorting-searching/rules/sorting-searching-for-searching-in-a-sorted-collection-always-prefer-binary.md
---
title: "For searching in a sorted collection, always prefer binary..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: sorting-searching, dev, algorithms, sorting-algorithm-selection, searching-algorithm-selection, understanding-sort-stability
---
## For searching in a sorted collection, always prefer binary...
For searching in a sorted collection, always prefer binary search over linear search.
algorithms/sorting-searching/rules/sorting-searching-reference-knuth-s-taocp-vol.md
---
title: "Reference Knuth's TAOCP Vol"
impact: MEDIUM
impactDescription: "general best practice"
tags: sorting-searching, dev, algorithms, sorting-algorithm-selection, searching-algorithm-selection, understanding-sort-stability
---
## Reference Knuth's TAOCP Vol
Reference Knuth's TAOCP Vol. 3 for rigorous analysis of any sorting or searching method.
algorithms/sorting-searching/rules/sorting-searching-the-sliding-window-technique-converts-many-o-n-2.md
---
title: "The sliding window technique converts many O(n^2)..."
impact: MEDIUM
impactDescription: "general best practice"
tags: sorting-searching, dev, algorithms, sorting-algorithm-selection, searching-algorithm-selection, understanding-sort-stability
---
## The sliding window technique converts many O(n^2)...
The sliding window technique converts many O(n^2) brute-force subarray problems into O(n).
algorithms/sorting-searching/rules/sorting-searching-use-your-language-s-built-in-sort-typically-timsort-or.md
---
title: "Use your language's built-in sort (typically Timsort or..."
impact: MEDIUM
impactDescription: "general best practice"
tags: sorting-searching, dev, algorithms, sorting-algorithm-selection, searching-algorithm-selection, understanding-sort-stability
---
## Use your language's built-in sort (typically Timsort or...
Use your language's built-in sort (typically Timsort or Introsort) unless you have a specific reason not to -- they are highly optimized.
algorithms/sorting-searching/rules/sorting-searching-when-data-has-bounded-integer-keys-consider-counting-or.md
---
title: "When data has bounded integer keys, consider counting or..."
impact: LOW
impactDescription: "recommended but situational"
tags: sorting-searching, dev, algorithms, sorting-algorithm-selection, searching-algorithm-selection, understanding-sort-stability
---
## When data has bounded integer keys, consider counting or...
When data has bounded integer keys, consider counting or radix sort for linear-time performance.
algorithms/sorting-searching/SKILL.md
---
name: sorting-searching
description: |
Use when implementing or selecting sorting and searching algorithms. Covers comparison sorts (Quicksort, Mergesort, Heapsort, Insertion sort, Timsort), linear sorts (Counting, Radix, Bucket), and searching techniques (binary search, interpolation search, two pointers, sliding window). Based on Knuth's TAOCP Vol. 3.
USE FOR: sorting algorithm selection, searching algorithm selection, understanding sort stability, complexity comparison of sorting methods, binary search variations, two-pointer and sliding window techniques
DO NOT USE FOR: graph traversal (use graph-algorithms), dynamic programming (use dynamic-programming)
license: MIT
metadata:
displayName: "Sorting & Searching Algorithms"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "The Art of Computer Programming, Vol. 3: Sorting and Searching — Donald Knuth"
url: "https://www-cs-faculty.stanford.edu/~knuth/taocp.html"
- title: "Sorting Algorithm — Wikipedia"
url: "https://en.wikipedia.org/wiki/Sorting_algorithm"
---
# Sorting & Searching Algorithms
## Overview
Sorting and searching are among the most fundamental operations in computer science. Knuth dedicated the entirety of *The Art of Computer Programming, Volume 3: Sorting and Searching* to these topics, reflecting their importance and depth. This skill covers the major algorithms in both categories, their complexities, and guidance on when to use each.
## Sorting Algorithms
### Comparison Sorts
Comparison-based sorts determine order by comparing pairs of elements. The theoretical lower bound for comparison sorting is O(n log n).
#### Quicksort
- **Strategy**: Divide and conquer. Pick a pivot, partition the array so elements less than the pivot come before it and elements greater come after, then recurse.
- **Time**: O(n log n) average, O(n^2) worst case (poor pivot choice)
- **Space**: O(log n) stack space (in-place partitioning)
- **Stable**: No
- **Best for**: General-purpose sorting; fastest in practice for most data distributions due to excellent cache behavior.
```
QUICKSORT(A, lo, hi):
if lo < hi:
p = PARTITION(A, lo, hi)
QUICKSORT(A, lo, p - 1)
QUICKSORT(A, p + 1, hi)
PARTITION(A, lo, hi):
pivot = A[hi]
i = lo - 1
for j = lo to hi - 1:
if A[j] <= pivot:
i = i + 1
swap A[i] and A[j]
swap A[i + 1] and A[hi]
return i + 1
```
#### Mergesort
- **Strategy**: Divide and conquer. Split the array in half, recursively sort each half, then merge the two sorted halves.
- **Time**: O(n log n) in all cases
- **Space**: O(n) auxiliary
- **Stable**: Yes
- **Best for**: When stability is required, linked lists, external sorting (large files that do not fit in memory).
```
MERGESORT(A, lo, hi):
if lo < hi:
mid = (lo + hi) / 2
MERGESORT(A, lo, mid)
MERGESORT(A, mid + 1, hi)
MERGE(A, lo, mid, hi)
MERGE(A, lo, mid, hi):
create temporary arrays L = A[lo..mid], R = A[mid+1..hi]
i = 0, j = 0, k = lo
while i < |L| and j < |R|:
if L[i] <= R[j]:
A[k] = L[i]; i++
else:
A[k] = R[j]; j++
k++
copy remaining elements of L or R into A
```
#### Heapsort
- **Strategy**: Build a max-heap from the array, then repeatedly extract the maximum to build the sorted result.
- **Time**: O(n log n) in all cases
- **Space**: O(1) (in-place)
- **Stable**: No
- **Best for**: When guaranteed O(n log n) is needed without extra memory. Not cache-friendly, so often slower than Quicksort in practice.
#### Insertion Sort
- **Strategy**: Build the sorted array one element at a time by inserting each element into its correct position among the already-sorted elements.
- **Time**: O(n) best case (nearly sorted), O(n^2) worst/average case
- **Space**: O(1) (in-place)
- **Stable**: Yes
- **Best for**: Small arrays, nearly sorted data, online sorting (data arrives one element at a time). Often used as the base case in hybrid sorts.
#### Timsort
- **Strategy**: Hybrid of Mergesort and Insertion sort. Identifies natural runs (already sorted subsequences), extends them with insertion sort, then merges runs using an optimized merge procedure.
- **Time**: O(n) best case (already sorted), O(n log n) worst case
- **Space**: O(n) auxiliary
- **Stable**: Yes
- **Best for**: Real-world data that often contains partially sorted subsequences. Default sort in Python and Java.
### Linear Sorts
These sorts exploit constraints on the input (bounded range, integer keys) to beat the O(n log n) comparison lower bound.
#### Counting Sort
- **Strategy**: Count occurrences of each value, then compute positions from cumulative counts.
- **Time**: O(n + k) where k is the range of input values
- **Space**: O(n + k)
- **Stable**: Yes
- **Best for**: Small integer ranges (e.g., sorting grades 0-100, characters).
#### Radix Sort
- **Strategy**: Sort by each digit/character position from least significant to most significant, using a stable sub-sort (typically counting sort) at each position.
- **Time**: O(d * (n + k)) where d is the number of digits and k is the radix
- **Space**: O(n + k)
- **Stable**: Yes
- **Best for**: Fixed-length integers or strings with bounded alphabet.
#### Bucket Sort
- **Strategy**: Distribute elements into buckets based on value range, sort each bucket individually, then concatenate.
- **Time**: O(n + k) average when input is uniformly distributed; O(n^2) worst case
- **Space**: O(n + k)
- **Stable**: Depends on bucket sort
- **Best for**: Uniformly distributed floating-point numbers in a known range.
### Sorting Complexity Comparison
| Algorithm | Best | Average | Worst | Space | Stable |
|-----------|------|---------|-------|-------|--------|
| Quicksort | O(n log n) | O(n log n) | O(n^2) | O(log n) | No |
| Mergesort | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes |
| Heapsort | O(n log n) | O(n log n) | O(n log n) | O(1) | No |
| Insertion Sort | O(n) | O(n^2) | O(n^2) | O(1) | Yes |
| Timsort | O(n) | O(n log n) | O(n log n) | O(n) | Yes |
| Counting Sort | O(n + k) | O(n + k) | O(n + k) | O(n + k) | Yes |
| Radix Sort | O(d(n + k)) | O(d(n + k)) | O(d(n + k)) | O(n + k) | Yes |
| Bucket Sort | O(n + k) | O(n + k) | O(n^2) | O(n + k) | Varies |
### When to Use Which Sort
| Situation | Recommended Sort |
|-----------|-----------------|
| General purpose, in-memory | Quicksort (or language default, often Timsort) |
| Stability required | Mergesort or Timsort |
| Guaranteed O(n log n), no extra space | Heapsort |
| Small arrays (n < 20) | Insertion Sort |
| Nearly sorted data | Insertion Sort or Timsort |
| Integer keys with small range | Counting Sort |
| Fixed-length integer/string keys | Radix Sort |
| Uniformly distributed floating-point data | Bucket Sort |
| External sorting (data on disk) | External Mergesort |
| Linked list sorting | Mergesort |
## Searching Algorithms
### Binary Search
- **Precondition**: Array must be sorted.
- **Strategy**: Repeatedly halve the search space by comparing the target to the middle element.
- **Time**: O(log n)
- **Space**: O(1) iterative, O(log n) recursive
```
BINARY_SEARCH(A, target):
lo = 0, hi = len(A) - 1
while lo <= hi:
mid = lo + (hi - lo) / 2
if A[mid] == target:
return mid
else if A[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1 // not found
```
**Variations**: Lower bound (first occurrence), upper bound (last occurrence), search on answer (binary search the solution space).
### Interpolation Search
- **Precondition**: Array must be sorted and values uniformly distributed.
- **Strategy**: Estimate the position of the target based on its value relative to the min and max of the current range.
- **Time**: O(log log n) average for uniformly distributed data, O(n) worst case
- **Best for**: Large, uniformly distributed sorted datasets.
### Two Pointers Technique
- **Strategy**: Use two pointers (indices) that move through the data structure, typically from opposite ends or at different speeds.
- **Time**: Typically O(n)
- **Common applications**: Pair sum in sorted array, removing duplicates, partitioning, palindrome check, merging sorted arrays.
### Sliding Window Technique
- **Strategy**: Maintain a window (contiguous subarray) that slides through the array, expanding or shrinking to satisfy a condition.
- **Time**: Typically O(n)
- **Common applications**: Maximum/minimum subarray of size k, longest substring without repeating characters, smallest subarray with sum >= target.
### Searching Complexity Comparison
| Algorithm | Average | Worst | Precondition |
|-----------|---------|-------|--------------|
| Linear Search | O(n) | O(n) | None |
| Binary Search | O(log n) | O(log n) | Sorted |
| Interpolation Search | O(log log n) | O(n) | Sorted, uniform distribution |
| Hash Lookup | O(1) | O(n) | Hash table built |
| Two Pointers | O(n) | O(n) | Often sorted |
| Sliding Window | O(n) | O(n) | Contiguous subarray problems |
## Stability in Sorting
A sort is **stable** if elements with equal keys retain their original relative order after sorting. Stability matters when:
- Sorting by multiple keys (sort by secondary key first, then by primary key with a stable sort).
- Preserving meaningful insertion order.
- Composing sorts for multi-level ordering.
## Best Practices
- Use your language's built-in sort (typically Timsort or Introsort) unless you have a specific reason not to -- they are highly optimized.
- For searching in a sorted collection, always prefer binary search over linear search.
- Consider the two pointers technique before reaching for nested loops on sorted data.
- The sliding window technique converts many O(n^2) brute-force subarray problems into O(n).
- When data has bounded integer keys, consider counting or radix sort for linear-time performance.
- Reference Knuth's TAOCP Vol. 3 for rigorous analysis of any sorting or searching method.
architecture/AGENTS.md
# Software Architecture
## Overview
Software architecture is the set of significant decisions about the organization of a software system -- the selection of structural elements, their interfaces, composition, and the guiding principles that constrain design and evolution over time. Choosing the right architecture style is one of the highest-leverage decisions a team makes; it shapes every subsequent technical and organizational choice.
This skill covers how to reason about architecture styles, evaluate architecture characteristics (quality attributes), and make informed tradeoffs. For deep dives into specific styles, see the sub-skills below.
## Canonical Works
| Book | Author(s) | Focus |
|------|-----------|-------|
| *Fundamentals of Software Architecture* | Mark Richards & Neal Ford | Architecture styles, characteristics, decisions, metrics |
| *Software Architecture: The Hard Parts* | Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani | Tradeoff analysis, decomposition, data ownership, contracts |
| *Building Evolutionary Architectures* | Ford, Parsons, Kua | Fitness functions, incremental change, governed evolution |
| *Documenting Software Architectures* | Clements et al. | Views, viewpoints, architecture documentation |
## The Monolith-to-Microservices Spectrum
Architecture is not a binary choice between monolith and microservices. It is a spectrum of modularity:
```
Monolith Microservices
| |
| Big Ball Layered Modular Service- Micro- |
| of Mud Monolith Monolith Based services |
| |
◄─────────────────────────────────────────────────────────────►
Less distributed More distributed
Simpler operations Complex operations
Easier consistency Eventual consistency
Tighter coupling Loose coupling
```
**Key insight (Richards & Ford):** Move along the spectrum only when the pain of your current position exceeds the cost of the next step. Start simple; evolve when you have evidence.
## Architecture Characteristics (Quality Attributes)
Architecture characteristics -- also called "-ilities" -- are the non-functional requirements that shape which style fits. They are inherently in tension; optimizing one often degrades another.
| Characteristic | Description | Tension With |
|---------------|-------------|--------------|
| **Scalability** | Ability to handle growing load | Simplicity, Cost |
| **Reliability** | System uptime and fault tolerance | Performance, Cost |
| **Performance** | Latency and throughput | Scalability, Maintainability |
| **Security** | Protection against threats | Usability, Performance |
| **Maintainability** | Ease of change and evolution | Performance, Time-to-Market |
| **Deployability** | Ease and frequency of deployment | Simplicity, Reliability |
| **Testability** | Ease of verifying correctness | Time-to-Market |
| **Elasticity** | Ability to scale up AND down dynamically | Cost, Simplicity |
| **Fault Tolerance** | Graceful degradation under failure | Performance, Complexity |
| **Modularity** | Degree of separation between components | Performance (indirection cost) |
### Identifying Driving Characteristics
Not every characteristic matters equally. Richards & Ford recommend identifying the **top 3-5 driving characteristics** for a system and using those to select an architecture style.
## Architecture Style Comparison
| Style | Scalability | Simplicity | Deployability | Data Consistency | Cost | Best For |
|-------|:-----------:|:----------:|:-------------:|:----------------:|:----:|----------|
| **Layered Monolith** | Low | High | Low | High | Low | Small teams, simple domains |
| **Modular Monolith** | Medium | Medium | Medium | High | Low | Medium complexity, single team |
| **Service-Based** | Medium | Medium | Medium | Medium | Medium | Domain-separated teams |
| **Microservices** | High | Low | High | Low | High | Large orgs, independent teams |
| **Event-Driven** | High | Low | High | Low | Medium | Async workflows, event streams |
| **Space-Based** | Very High | Low | Medium | Low | High | Extreme elastic scalability |
| **Orchestration-Driven** | Medium | Medium | Medium | Medium | Medium | Complex workflows |
| **Pipeline (Pipes & Filters)** | Medium | Medium | Medium | Medium | Low | Data processing, ETL |
## Architecture Decision Records (ADRs)
Every significant architecture decision should be captured in an Architecture Decision Record. ADRs provide context for future developers about why a decision was made, what alternatives were considered, and what tradeoffs were accepted.
See: `specs/documentation/adr` for ADR templates and practices.
**ADR structure (Michael Nygard format):**
- **Title** -- Short noun phrase (e.g., "Use PostgreSQL for order data")
- **Status** -- Proposed, Accepted, Deprecated, Superseded
- **Context** -- The forces at play, the problem, the constraints
- **Decision** -- What was decided and why
- **Consequences** -- What becomes easier, what becomes harder
## Architecture Decision Process
1. **Identify the driving characteristics** -- What are the top 3-5 quality attributes?
2. **Identify the domain partitioning** -- How does the domain decompose? (see `dev/architecture/domain-driven-design`)
3. **Select a candidate style** -- Use the comparison table above to narrow options.
4. **Evaluate tradeoffs** -- Every style has strengths and weaknesses. Make tradeoffs explicit.
5. **Record the decision** -- Write an ADR capturing context, decision, and consequences.
6. **Validate with fitness functions** -- Define measurable criteria that the architecture must satisfy over time.
## Common Anti-Patterns
- **Accidental architecture** -- No deliberate style; the system evolves into a Big Ball of Mud.
- **Resume-driven architecture** -- Choosing microservices (or any style) because it looks good on a resume, not because the problem demands it.
- **Architecture by analogy** -- "Netflix uses microservices, so we should too." Your context is not Netflix's context.
- **Ignoring the First Law of Software Architecture** -- "Everything in software architecture is a tradeoff" (Richards & Ford). If you think you found something that isn't a tradeoff, you haven't identified the tradeoff yet.
## Best Practices
- Start with the simplest architecture that meets your driving characteristics. Evolve when evidence demands it.
- Make architecture decisions explicit and documented (ADRs).
- Architecture is not a one-time activity -- it is continuous. Revisit decisions as the system and context evolve.
- Align architecture boundaries with team boundaries (see Conway's Law and the Inverse Conway Maneuver).
- Use fitness functions to objectively measure whether the architecture meets its goals over time.
- Understand that the "best" architecture depends on your specific context: team size, domain complexity, scale requirements, and organizational structure.
## Sub-Skills
- `dev/architecture/microservices` -- Service decomposition, inter-service communication, saga patterns
- `dev/architecture/monoliths` -- Modular monolith, monolith-first strategy, Strangler Fig migration
- `dev/architecture/well-architected` -- AWS, Azure, and GCP well-architected frameworks
- `dev/architecture/event-driven` -- Event-driven architecture, event sourcing, CQRS
- `dev/architecture/domain-driven-design` -- Bounded contexts, aggregates, strategic and tactical DDD
- `dev/architecture/hexagonal` -- Ports and adapters, onion architecture, dependency inversion
architecture/domain-driven-design/AGENTS.md
# Domain-Driven Design (DDD)
## Overview
Domain-Driven Design is a software design approach that centers the development process on the core business domain. It provides both strategic patterns for organizing large systems and tactical patterns for modeling individual domains. DDD is especially valuable for complex domains where the business logic is the primary source of difficulty.
The canonical reference is Eric Evans' *Domain-Driven Design: Tackling Complexity in the Heart of Software* (2003), supplemented by Vaughn Vernon's *Implementing Domain-Driven Design* (2013) and *Domain-Driven Design Distilled* (2016).
**Core premise:** The structure of the software should mirror the structure of the business domain. The language used by developers should be the same language used by domain experts.
## Strategic DDD
Strategic DDD deals with the big picture: how to decompose a large system into manageable parts, how those parts relate to each other, and how teams communicate across boundaries.
### Ubiquitous Language
A shared, precise language between developers and domain experts for each bounded context. The same term means the same thing everywhere within a context -- in conversations, documentation, code, and tests.
**Rules:**
- One bounded context, one ubiquitous language.
- If a term means different things to different people, you likely have multiple bounded contexts.
- The language should appear literally in the code: class names, method names, variable names.
- Refine the language continuously as understanding deepens.
**Example:** In an e-commerce system, "Order" means different things in different contexts:
- **Sales context:** An Order is a customer's purchase intent with line items and pricing.
- **Fulfillment context:** An Order is a set of items to pick, pack, and ship.
- **Billing context:** An Order is an invoice with payment terms.
Each context has its own Order model with its own ubiquitous language.
### Bounded Contexts
A bounded context is an explicit boundary within which a domain model is defined and applicable. Inside a bounded context, the ubiquitous language is consistent. Across bounded contexts, the same word may mean different things.
```
┌─────────────────────────────────────────────────────┐
│ E-Commerce System │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Sales │ │ Fulfillment │ │ Billing │ │
│ │ Context │ │ Context │ │ Context │ │
│ │ │ │ │ │ │ │
│ │ Order = │ │ Order = │ │ Order = │ │
│ │ purchase │ │ shipment │ │ invoice │ │
│ │ intent │ │ items │ │ │ │
│ │ │ │ │ │ Customer = │ │
│ │ Customer = │ │ Customer = │ │ billing │ │
│ │ buyer with │ │ shipping │ │ account │ │
│ │ preferences │ │ address │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ Each context has its own model, language, and data │
└─────────────────────────────────────────────────────┘
```
### Subdomains
Subdomains represent areas of the business. They exist independently of software -- they are about the business problem space, not the solution.
| Type | Description | Investment Strategy | Example |
|------|-------------|-------------------|---------|
| **Core** | Your competitive advantage; what differentiates the business | Build custom; invest the best talent | Pricing engine for an insurance company |
| **Supporting** | Necessary for the business but not differentiating | Build simpler custom solutions or customize off-the-shelf | Customer onboarding |
| **Generic** | Common to many businesses; commodity | Buy or use open-source | Authentication, email, payment processing |
**Key insight:** Align bounded contexts with subdomains where possible. Invest the most effort in core subdomains.
### Context Maps
A context map is a visualization of the relationships between bounded contexts. It shows how contexts integrate and what the power dynamics are.
```
┌──────────────┐ ┌──────────────┐
│ Sales │ │ Fulfillment │
│ (Core) │─────────▶│ (Supporting) │
│ │ Customer │ │
│ │ -Supplier│ │
└──────┬───────┘ └──────────────┘
│
│ Published
│ Language
│
┌──────▼───────┐ ┌──────────────┐
│ Billing │ │ Shipping │
│ (Core) │◀─────────│ (Generic) │
│ │ ACL │ (3rd party) │
└──────────────┘ └──────────────┘
```
### Context Mapping Patterns
| Pattern | Description | When to Use |
|---------|-------------|-------------|
| **Shared Kernel** | Two contexts share a subset of the model (code, schema). Changes require coordination. | Closely collaborating teams; small shared concepts |
| **Customer-Supplier** | Upstream (supplier) provides what downstream (customer) needs. Customer can influence the API. | Teams with a cooperative relationship; downstream has negotiation power |
| **Conformist** | Downstream conforms to upstream's model without negotiation. | Upstream won't change for you (e.g., large legacy system, external API) |
| **Anti-Corruption Layer (ACL)** | Downstream translates the upstream model into its own model via a translation layer. | Protecting your domain model from a foreign or legacy model |
| **Open Host Service** | Upstream provides a well-defined, versioned API (protocol) for many consumers. | Serving multiple downstream contexts; public APIs |
| **Published Language** | A shared, documented data format (e.g., JSON schema, Protobuf, XML schema) used for integration. | Standardized exchange format; often combined with Open Host Service |
| **Separate Ways** | Contexts have no integration; they solve their own problems independently. | When integration cost exceeds benefit |
| **Partnership** | Two contexts evolve together with mutual coordination. Neither is upstream or downstream. | Co-developing teams with aligned release cadences |
## Tactical DDD
Tactical DDD provides the building blocks for modeling a single bounded context.
### Entities
Objects defined by their **identity**, not their attributes. Two entities with the same attributes but different IDs are different entities. Entities have a lifecycle and mutable state.
```
// An Order is identified by its OrderId, not its contents
public class Order
{
public OrderId Id { get; }
public CustomerId CustomerId { get; }
public List<OrderLine> Lines { get; }
public OrderStatus Status { get; private set; }
public void Confirm() { ... }
public void Cancel() { ... }
}
```
### Value Objects
Objects defined by their **attributes**, not by identity. Two value objects with the same attributes are equal. Value objects are immutable.
```
// A Money value is defined by its amount and currency
public record Money(decimal Amount, string Currency)
{
public Money Add(Money other)
{
if (Currency != other.Currency)
throw new CurrencyMismatchException();
return new Money(Amount + other.Amount, Currency);
}
}
```
**Prefer value objects over entities.** Most concepts in a domain are values, not entities. Using value objects reduces bugs (immutability) and improves clarity.
### Aggregates and Aggregate Roots
An aggregate is a cluster of entities and value objects treated as a single unit for data changes. The **aggregate root** is the entry point -- all external access goes through the root. The root enforces invariants (business rules) for the entire aggregate.
```
┌──────────────────────────────────────┐
│ Order Aggregate │
│ │
│ ┌────────────────┐ │
│ │ Order │ ◄── Aggregate │
│ │ (Root) │ Root │
│ │ │ │
│ │ - orderId │ │
│ │ - status │ │
│ │ - totalAmount │ │
│ └───┬────────────┘ │
│ │ contains │
│ │ │
│ ┌───▼────────────┐ ┌───────────┐ │
│ │ OrderLine │ │ Money │ │
│ │ (Entity) │ │ (Value) │ │
│ │ - lineId │ │ - amount │ │
│ │ - productId │ │ - currency│ │
│ │ - quantity │ │ │ │
│ └────────────────┘ └───────────┘ │
│ │
│ Invariant: total = sum of lines │
│ Invariant: max 20 lines per order │
└──────────────────────────────────────┘
```
### Aggregate Design Rules
1. **Reference other aggregates by identity only.** An Order aggregate holds a `CustomerId`, not a `Customer` object.
2. **Keep aggregates small.** Large aggregates cause contention, slow loading, and complex invariants. Prefer small aggregates with eventual consistency between them.
3. **One transaction per aggregate.** Modify only one aggregate per transaction. Use domain events for cross-aggregate coordination.
4. **Protect invariants within the aggregate boundary.** Business rules that span multiple aggregates must be handled via eventual consistency (domain events, sagas).
### Aggregate Design Example
```
// GOOD: Small aggregates, reference by ID, domain events
public class Order // Aggregate root
{
public OrderId Id { get; }
private List<OrderLine> _lines = new();
public void AddLine(ProductId productId, int quantity, Money price)
{
if (_lines.Count >= 20)
throw new TooManyLinesException();
_lines.Add(new OrderLine(productId, quantity, price));
AddDomainEvent(new OrderLineAdded(Id, productId, quantity));
}
public void Confirm()
{
if (!_lines.Any()) throw new EmptyOrderException();
Status = OrderStatus.Confirmed;
AddDomainEvent(new OrderConfirmed(Id, TotalAmount));
}
}
public class Inventory // Separate aggregate
{
public ProductId ProductId { get; }
public int AvailableQuantity { get; private set; }
// Reacts to OrderConfirmed event (eventual consistency)
public void Reserve(int quantity)
{
if (AvailableQuantity < quantity)
throw new InsufficientStockException();
AvailableQuantity -= quantity;
AddDomainEvent(new StockReserved(ProductId, quantity));
}
}
```
### Domain Events
Events that represent something significant that happened in the domain. Domain events enable loose coupling between aggregates and bounded contexts.
**Naming convention:** Past tense, describing what happened -- `OrderPlaced`, `PaymentReceived`, `ShipmentDispatched`.
See `dev/architecture/event-driven` for event sourcing and event-driven architecture patterns.
### Repositories
Provide collection-like access to aggregates. One repository per aggregate root. Repositories abstract the persistence mechanism.
```
public interface IOrderRepository
{
Task<Order?> GetById(OrderId id);
Task Save(Order order);
Task Delete(OrderId id);
// No query methods here -- queries belong in the read model (CQRS)
}
```
### Domain Services
Operations that don't naturally belong to any single entity or value object. Domain services are stateless and express domain logic.
```
// Pricing logic that spans multiple aggregates
public class PricingService
{
public Money CalculateDiscount(
Order order, CustomerTier tier, IReadOnlyList<Promotion> activePromotions)
{
// Complex pricing logic that doesn't belong in Order or Customer
}
}
```
### Application Services
Orchestrate use cases by coordinating domain objects, repositories, and infrastructure concerns. Application services are the entry point from the outside world (API controllers, message handlers) into the domain.
```
public class PlaceOrderHandler
{
public async Task Handle(PlaceOrderCommand command)
{
var order = new Order(command.CustomerId);
foreach (var item in command.Items)
order.AddLine(item.ProductId, item.Quantity, item.Price);
order.Confirm();
await _orderRepository.Save(order);
await _eventPublisher.Publish(order.DomainEvents);
}
}
```
### Factories
Encapsulate complex aggregate creation logic. Use factories when object construction involves business rules, validation, or coordination.
## Strategic + Tactical DDD Together
```
Strategic (System Level):
Identify Subdomains → Define Bounded Contexts → Map Context Relationships
Tactical (Within Each Context):
Model Aggregates → Define Entities & Value Objects →
Publish Domain Events → Implement Repositories & Services
```
## Common DDD Anti-Patterns
| Anti-Pattern | Problem | Solution |
|-------------|---------|----------|
| **Anemic Domain Model** | Entities are just data bags; logic lives in services | Move behavior into entities and value objects |
| **God Aggregate** | One massive aggregate with many entities | Break into smaller aggregates; use eventual consistency |
| **Shared Database across Contexts** | Bounded contexts lose independence | Each context owns its data; integrate through events or APIs |
| **Ubiquitous Language mismatch** | Code uses different terms than domain experts | Refactor code to match the domain language exactly |
| **DDD everywhere** | Applying DDD to simple CRUD domains | Use DDD for core subdomains; use simpler approaches for generic/supporting |
## Best Practices
- Apply DDD only where the domain complexity justifies it (core subdomains). For CRUD-heavy generic subdomains, simpler approaches are fine.
- Invest heavily in ubiquitous language. If developers and domain experts use different words, the design will drift.
- Keep aggregates small. The default should be a single entity as the aggregate root. Add more only when invariants require it.
- Reference other aggregates by identity, never by direct object reference.
- Use domain events for cross-aggregate and cross-context communication.
- Collaborate with domain experts continuously -- DDD is not a solo developer activity.
- Draw context maps early and revisit them as the system evolves.
- Bounded context boundaries often align well with microservice boundaries (see `dev/architecture/microservices`), but they don't have to -- a modular monolith can also respect bounded contexts (see `dev/architecture/monoliths`).
architecture/domain-driven-design/metadata.json
{
"version": "1.0.0",
"name": "domain-driven-design",
"displayName": "Domain-Driven Design",
"description": "Domain-Driven Design (DDD) strategic and tactical patterns based on Eric Evans' \"Domain-Driven Design\" -- covering bounded contexts, aggregates, context maps, and ubiquitous language for modeling complex domains.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "Martin Fowler — Domain-Driven Design",
"url": "https://martinfowler.com/bliki/DomainDrivenDesign.html"
},
{
"title": "Domain-Driven Design — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Domain-driven_design"
}
]
}
architecture/domain-driven-design/README.md
# Domain-Driven Design
Domain-Driven Design (DDD) strategic and tactical patterns based on Eric Evans' "Domain-Driven Design" -- covering bounded contexts, aggregates, context maps, and ubiquitous language for modeling complex domains.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 8 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/architecture/domain-driven-design
```
## License
MIT
architecture/domain-driven-design/rules/_sections.md
# Domain-Driven Design Rules
Best practices and rules for Domain-Driven Design.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Apply DDD only where the domain complexity justifies it... | MEDIUM | [`domain-driven-design-apply-ddd-only-where-the-domain-complexity-justifies-it.md`](domain-driven-design-apply-ddd-only-where-the-domain-complexity-justifies-it.md) |
| 2 | Invest heavily in ubiquitous language | MEDIUM | [`domain-driven-design-invest-heavily-in-ubiquitous-language.md`](domain-driven-design-invest-heavily-in-ubiquitous-language.md) |
| 3 | Keep aggregates small | HIGH | [`domain-driven-design-keep-aggregates-small.md`](domain-driven-design-keep-aggregates-small.md) |
| 4 | Reference other aggregates by identity, never by direct... | CRITICAL | [`domain-driven-design-reference-other-aggregates-by-identity-never-by-direct.md`](domain-driven-design-reference-other-aggregates-by-identity-never-by-direct.md) |
| 5 | Use domain events for cross-aggregate and cross-context... | MEDIUM | [`domain-driven-design-use-domain-events-for-cross-aggregate-and-cross-context.md`](domain-driven-design-use-domain-events-for-cross-aggregate-and-cross-context.md) |
| 6 | Collaborate with domain experts continuously -- DDD is not... | MEDIUM | [`domain-driven-design-collaborate-with-domain-experts-continuously-ddd-is-not.md`](domain-driven-design-collaborate-with-domain-experts-continuously-ddd-is-not.md) |
| 7 | Draw context maps early and revisit them as the system... | MEDIUM | [`domain-driven-design-draw-context-maps-early-and-revisit-them-as-the-system.md`](domain-driven-design-draw-context-maps-early-and-revisit-them-as-the-system.md) |
| 8 | Bounded context boundaries often align well with... | MEDIUM | [`domain-driven-design-bounded-context-boundaries-often-align-well-with.md`](domain-driven-design-bounded-context-boundaries-often-align-well-with.md) |
architecture/domain-driven-design/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: domain-driven-design, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
architecture/domain-driven-design/rules/domain-driven-design-apply-ddd-only-where-the-domain-complexity-justifies-it.md
---
title: "Apply DDD only where the domain complexity justifies it..."
impact: MEDIUM
impactDescription: "general best practice"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Apply DDD only where the domain complexity justifies it...
Apply DDD only where the domain complexity justifies it (core subdomains). For CRUD-heavy generic subdomains, simpler approaches are fine.
architecture/domain-driven-design/rules/domain-driven-design-bounded-context-boundaries-often-align-well-with.md
---
title: "Bounded context boundaries often align well with..."
impact: MEDIUM
impactDescription: "general best practice"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Bounded context boundaries often align well with...
Bounded context boundaries often align well with microservice boundaries (see `dev/architecture/microservices`), but they don't have to -- a modular monolith can also respect bounded contexts (see `dev/architecture/monoliths`).
architecture/domain-driven-design/rules/domain-driven-design-collaborate-with-domain-experts-continuously-ddd-is-not.md
---
title: "Collaborate with domain experts continuously -- DDD is not..."
impact: MEDIUM
impactDescription: "general best practice"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Collaborate with domain experts continuously -- DDD is not...
Collaborate with domain experts continuously -- DDD is not a solo developer activity.
architecture/domain-driven-design/rules/domain-driven-design-draw-context-maps-early-and-revisit-them-as-the-system.md
---
title: "Draw context maps early and revisit them as the system..."
impact: MEDIUM
impactDescription: "general best practice"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Draw context maps early and revisit them as the system...
Draw context maps early and revisit them as the system evolves.
architecture/domain-driven-design/rules/domain-driven-design-invest-heavily-in-ubiquitous-language.md
---
title: "Invest heavily in ubiquitous language"
impact: MEDIUM
impactDescription: "general best practice"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Invest heavily in ubiquitous language
Invest heavily in ubiquitous language. If developers and domain experts use different words, the design will drift.
architecture/domain-driven-design/rules/domain-driven-design-keep-aggregates-small.md
---
title: "Keep aggregates small"
impact: HIGH
impactDescription: "significant quality or reliability improvement"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Keep aggregates small
Keep aggregates small. The default should be a single entity as the aggregate root. Add more only when invariants require it.
architecture/domain-driven-design/rules/domain-driven-design-reference-other-aggregates-by-identity-never-by-direct.md
---
title: "Reference other aggregates by identity, never by direct..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Reference other aggregates by identity, never by direct...
Reference other aggregates by identity, never by direct object reference.
architecture/domain-driven-design/rules/domain-driven-design-use-domain-events-for-cross-aggregate-and-cross-context.md
---
title: "Use domain events for cross-aggregate and cross-context..."
impact: MEDIUM
impactDescription: "general best practice"
tags: domain-driven-design, dev, architecture, bounded-context-identification, context-mapping, aggregate-design
---
## Use domain events for cross-aggregate and cross-context...
Use domain events for cross-aggregate and cross-context communication.
architecture/domain-driven-design/SKILL.md
---
name: domain-driven-design
description: |
Domain-Driven Design (DDD) strategic and tactical patterns based on Eric Evans' "Domain-Driven Design" -- covering bounded contexts, aggregates, context maps, and ubiquitous language for modeling complex domains.
USE FOR: bounded context identification, context mapping, aggregate design, ubiquitous language, domain modeling, subdomain classification, strategic domain design, tactical DDD patterns
DO NOT USE FOR: event sourcing mechanics (use event-driven), microservice decomposition (use microservices), hexagonal ports/adapters (use hexagonal)
license: MIT
metadata:
displayName: "Domain-Driven Design"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Martin Fowler — Domain-Driven Design"
url: "https://martinfowler.com/bliki/DomainDrivenDesign.html"
- title: "Domain-Driven Design — Wikipedia"
url: "https://en.wikipedia.org/wiki/Domain-driven_design"
---
# Domain-Driven Design (DDD)
## Overview
Domain-Driven Design is a software design approach that centers the development process on the core business domain. It provides both strategic patterns for organizing large systems and tactical patterns for modeling individual domains. DDD is especially valuable for complex domains where the business logic is the primary source of difficulty.
The canonical reference is Eric Evans' *Domain-Driven Design: Tackling Complexity in the Heart of Software* (2003), supplemented by Vaughn Vernon's *Implementing Domain-Driven Design* (2013) and *Domain-Driven Design Distilled* (2016).
**Core premise:** The structure of the software should mirror the structure of the business domain. The language used by developers should be the same language used by domain experts.
## Strategic DDD
Strategic DDD deals with the big picture: how to decompose a large system into manageable parts, how those parts relate to each other, and how teams communicate across boundaries.
### Ubiquitous Language
A shared, precise language between developers and domain experts for each bounded context. The same term means the same thing everywhere within a context -- in conversations, documentation, code, and tests.
**Rules:**
- One bounded context, one ubiquitous language.
- If a term means different things to different people, you likely have multiple bounded contexts.
- The language should appear literally in the code: class names, method names, variable names.
- Refine the language continuously as understanding deepens.
**Example:** In an e-commerce system, "Order" means different things in different contexts:
- **Sales context:** An Order is a customer's purchase intent with line items and pricing.
- **Fulfillment context:** An Order is a set of items to pick, pack, and ship.
- **Billing context:** An Order is an invoice with payment terms.
Each context has its own Order model with its own ubiquitous language.
### Bounded Contexts
A bounded context is an explicit boundary within which a domain model is defined and applicable. Inside a bounded context, the ubiquitous language is consistent. Across bounded contexts, the same word may mean different things.
```
┌─────────────────────────────────────────────────────┐
│ E-Commerce System │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Sales │ │ Fulfillment │ │ Billing │ │
│ │ Context │ │ Context │ │ Context │ │
│ │ │ │ │ │ │ │
│ │ Order = │ │ Order = │ │ Order = │ │
│ │ purchase │ │ shipment │ │ invoice │ │
│ │ intent │ │ items │ │ │ │
│ │ │ │ │ │ Customer = │ │
│ │ Customer = │ │ Customer = │ │ billing │ │
│ │ buyer with │ │ shipping │ │ account │ │
│ │ preferences │ │ address │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ Each context has its own model, language, and data │
└─────────────────────────────────────────────────────┘
```
### Subdomains
Subdomains represent areas of the business. They exist independently of software -- they are about the business problem space, not the solution.
| Type | Description | Investment Strategy | Example |
|------|-------------|-------------------|---------|
| **Core** | Your competitive advantage; what differentiates the business | Build custom; invest the best talent | Pricing engine for an insurance company |
| **Supporting** | Necessary for the business but not differentiating | Build simpler custom solutions or customize off-the-shelf | Customer onboarding |
| **Generic** | Common to many businesses; commodity | Buy or use open-source | Authentication, email, payment processing |
**Key insight:** Align bounded contexts with subdomains where possible. Invest the most effort in core subdomains.
### Context Maps
A context map is a visualization of the relationships between bounded contexts. It shows how contexts integrate and what the power dynamics are.
```
┌──────────────┐ ┌──────────────┐
│ Sales │ │ Fulfillment │
│ (Core) │─────────▶│ (Supporting) │
│ │ Customer │ │
│ │ -Supplier│ │
└──────┬───────┘ └──────────────┘
│
│ Published
│ Language
│
┌──────▼───────┐ ┌──────────────┐
│ Billing │ │ Shipping │
│ (Core) │◀─────────│ (Generic) │
│ │ ACL │ (3rd party) │
└──────────────┘ └──────────────┘
```
### Context Mapping Patterns
| Pattern | Description | When to Use |
|---------|-------------|-------------|
| **Shared Kernel** | Two contexts share a subset of the model (code, schema). Changes require coordination. | Closely collaborating teams; small shared concepts |
| **Customer-Supplier** | Upstream (supplier) provides what downstream (customer) needs. Customer can influence the API. | Teams with a cooperative relationship; downstream has negotiation power |
| **Conformist** | Downstream conforms to upstream's model without negotiation. | Upstream won't change for you (e.g., large legacy system, external API) |
| **Anti-Corruption Layer (ACL)** | Downstream translates the upstream model into its own model via a translation layer. | Protecting your domain model from a foreign or legacy model |
| **Open Host Service** | Upstream provides a well-defined, versioned API (protocol) for many consumers. | Serving multiple downstream contexts; public APIs |
| **Published Language** | A shared, documented data format (e.g., JSON schema, Protobuf, XML schema) used for integration. | Standardized exchange format; often combined with Open Host Service |
| **Separate Ways** | Contexts have no integration; they solve their own problems independently. | When integration cost exceeds benefit |
| **Partnership** | Two contexts evolve together with mutual coordination. Neither is upstream or downstream. | Co-developing teams with aligned release cadences |
## Tactical DDD
Tactical DDD provides the building blocks for modeling a single bounded context.
### Entities
Objects defined by their **identity**, not their attributes. Two entities with the same attributes but different IDs are different entities. Entities have a lifecycle and mutable state.
```
// An Order is identified by its OrderId, not its contents
public class Order
{
public OrderId Id { get; }
public CustomerId CustomerId { get; }
public List<OrderLine> Lines { get; }
public OrderStatus Status { get; private set; }
public void Confirm() { ... }
public void Cancel() { ... }
}
```
### Value Objects
Objects defined by their **attributes**, not by identity. Two value objects with the same attributes are equal. Value objects are immutable.
```
// A Money value is defined by its amount and currency
public record Money(decimal Amount, string Currency)
{
public Money Add(Money other)
{
if (Currency != other.Currency)
throw new CurrencyMismatchException();
return new Money(Amount + other.Amount, Currency);
}
}
```
**Prefer value objects over entities.** Most concepts in a domain are values, not entities. Using value objects reduces bugs (immutability) and improves clarity.
### Aggregates and Aggregate Roots
An aggregate is a cluster of entities and value objects treated as a single unit for data changes. The **aggregate root** is the entry point -- all external access goes through the root. The root enforces invariants (business rules) for the entire aggregate.
```
┌──────────────────────────────────────┐
│ Order Aggregate │
│ │
│ ┌────────────────┐ │
│ │ Order │ ◄── Aggregate │
│ │ (Root) │ Root │
│ │ │ │
│ │ - orderId │ │
│ │ - status │ │
│ │ - totalAmount │ │
│ └───┬────────────┘ │
│ │ contains │
│ │ │
│ ┌───▼────────────┐ ┌───────────┐ │
│ │ OrderLine │ │ Money │ │
│ │ (Entity) │ │ (Value) │ │
│ │ - lineId │ │ - amount │ │
│ │ - productId │ │ - currency│ │
│ │ - quantity │ │ │ │
│ └────────────────┘ └───────────┘ │
│ │
│ Invariant: total = sum of lines │
│ Invariant: max 20 lines per order │
└──────────────────────────────────────┘
```
### Aggregate Design Rules
1. **Reference other aggregates by identity only.** An Order aggregate holds a `CustomerId`, not a `Customer` object.
2. **Keep aggregates small.** Large aggregates cause contention, slow loading, and complex invariants. Prefer small aggregates with eventual consistency between them.
3. **One transaction per aggregate.** Modify only one aggregate per transaction. Use domain events for cross-aggregate coordination.
4. **Protect invariants within the aggregate boundary.** Business rules that span multiple aggregates must be handled via eventual consistency (domain events, sagas).
### Aggregate Design Example
```
// GOOD: Small aggregates, reference by ID, domain events
public class Order // Aggregate root
{
public OrderId Id { get; }
private List<OrderLine> _lines = new();
public void AddLine(ProductId productId, int quantity, Money price)
{
if (_lines.Count >= 20)
throw new TooManyLinesException();
_lines.Add(new OrderLine(productId, quantity, price));
AddDomainEvent(new OrderLineAdded(Id, productId, quantity));
}
public void Confirm()
{
if (!_lines.Any()) throw new EmptyOrderException();
Status = OrderStatus.Confirmed;
AddDomainEvent(new OrderConfirmed(Id, TotalAmount));
}
}
public class Inventory // Separate aggregate
{
public ProductId ProductId { get; }
public int AvailableQuantity { get; private set; }
// Reacts to OrderConfirmed event (eventual consistency)
public void Reserve(int quantity)
{
if (AvailableQuantity < quantity)
throw new InsufficientStockException();
AvailableQuantity -= quantity;
AddDomainEvent(new StockReserved(ProductId, quantity));
}
}
```
### Domain Events
Events that represent something significant that happened in the domain. Domain events enable loose coupling between aggregates and bounded contexts.
**Naming convention:** Past tense, describing what happened -- `OrderPlaced`, `PaymentReceived`, `ShipmentDispatched`.
See `dev/architecture/event-driven` for event sourcing and event-driven architecture patterns.
### Repositories
Provide collection-like access to aggregates. One repository per aggregate root. Repositories abstract the persistence mechanism.
```
public interface IOrderRepository
{
Task<Order?> GetById(OrderId id);
Task Save(Order order);
Task Delete(OrderId id);
// No query methods here -- queries belong in the read model (CQRS)
}
```
### Domain Services
Operations that don't naturally belong to any single entity or value object. Domain services are stateless and express domain logic.
```
// Pricing logic that spans multiple aggregates
public class PricingService
{
public Money CalculateDiscount(
Order order, CustomerTier tier, IReadOnlyList<Promotion> activePromotions)
{
// Complex pricing logic that doesn't belong in Order or Customer
}
}
```
### Application Services
Orchestrate use cases by coordinating domain objects, repositories, and infrastructure concerns. Application services are the entry point from the outside world (API controllers, message handlers) into the domain.
```
public class PlaceOrderHandler
{
public async Task Handle(PlaceOrderCommand command)
{
var order = new Order(command.CustomerId);
foreach (var item in command.Items)
order.AddLine(item.ProductId, item.Quantity, item.Price);
order.Confirm();
await _orderRepository.Save(order);
await _eventPublisher.Publish(order.DomainEvents);
}
}
```
### Factories
Encapsulate complex aggregate creation logic. Use factories when object construction involves business rules, validation, or coordination.
## Strategic + Tactical DDD Together
```
Strategic (System Level):
Identify Subdomains → Define Bounded Contexts → Map Context Relationships
Tactical (Within Each Context):
Model Aggregates → Define Entities & Value Objects →
Publish Domain Events → Implement Repositories & Services
```
## Common DDD Anti-Patterns
| Anti-Pattern | Problem | Solution |
|-------------|---------|----------|
| **Anemic Domain Model** | Entities are just data bags; logic lives in services | Move behavior into entities and value objects |
| **God Aggregate** | One massive aggregate with many entities | Break into smaller aggregates; use eventual consistency |
| **Shared Database across Contexts** | Bounded contexts lose independence | Each context owns its data; integrate through events or APIs |
| **Ubiquitous Language mismatch** | Code uses different terms than domain experts | Refactor code to match the domain language exactly |
| **DDD everywhere** | Applying DDD to simple CRUD domains | Use DDD for core subdomains; use simpler approaches for generic/supporting |
## Best Practices
- Apply DDD only where the domain complexity justifies it (core subdomains). For CRUD-heavy generic subdomains, simpler approaches are fine.
- Invest heavily in ubiquitous language. If developers and domain experts use different words, the design will drift.
- Keep aggregates small. The default should be a single entity as the aggregate root. Add more only when invariants require it.
- Reference other aggregates by identity, never by direct object reference.
- Use domain events for cross-aggregate and cross-context communication.
- Collaborate with domain experts continuously -- DDD is not a solo developer activity.
- Draw context maps early and revisit them as the system evolves.
- Bounded context boundaries often align well with microservice boundaries (see `dev/architecture/microservices`), but they don't have to -- a modular monolith can also respect bounded contexts (see `dev/architecture/monoliths`).
architecture/event-driven/AGENTS.md
# Event-Driven Architecture, Event Sourcing & CQRS
## Overview
This skill covers three complementary but independent patterns that are frequently used together:
1. **Event-Driven Architecture (EDA)** -- A system design where components communicate through events rather than direct calls.
2. **Event Sourcing** -- A persistence pattern where state is stored as a sequence of events rather than as current state.
3. **CQRS (Command Query Responsibility Segregation)** -- A pattern that separates read and write models.
These patterns can be used independently or combined. Understanding when to use each -- and when to combine them -- is critical.
## Canonical Works
| Book | Author(s) | Relevant Coverage |
|------|-----------|-------------------|
| *Designing Data-Intensive Applications* | Martin Kleppmann | Event sourcing, stream processing, change data capture |
| *Implementing Domain-Driven Design* | Vaughn Vernon | Domain events, event sourcing with DDD, CQRS |
| *Building Event-Driven Microservices* | Adam Bellemare | EDA at scale, event mesh, stream processing |
| *Enterprise Integration Patterns* | Hohpe & Woolf | Messaging foundations (see `dev/integration-patterns`) |
## Relationship Between the Three Patterns
```
┌─────────────────────────────────────────────────────────┐
│ │
│ Event-Driven Architecture (EDA) │
│ Components communicate through events │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Event Sourcing │ │ CQRS │ │
│ │ Store state as │ │ Separate read │ │
│ │ event log │ │ and write models │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ └───────────┬───────────┘ │
│ │ │
│ Can be combined but │
│ are independent patterns │
│ │
└─────────────────────────────────────────────────────────┘
```
- **EDA without Event Sourcing:** Services publish events but store current state in a traditional database.
- **Event Sourcing without CQRS:** Store state as events and rebuild current state from the event log -- but use the same model for reads and writes.
- **CQRS without Event Sourcing:** Separate read and write models backed by a traditional database.
- **All three combined:** The most powerful but most complex combination.
## Event-Driven Architecture (EDA)
In EDA, components produce and consume events. An event represents something that happened -- a fact. Events are immutable and past-tense (OrderPlaced, PaymentReceived, InventoryReserved).
### Event Types
| Type | Description | Example |
|------|-------------|---------|
| **Event Notification** | A thin signal that something happened; consumer fetches details if needed | `{ "type": "OrderPlaced", "orderId": "123" }` |
| **Event-Carried State Transfer** | Event carries the full state needed by consumers | `{ "type": "OrderPlaced", "orderId": "123", "items": [...], "total": 99.95 }` |
| **Domain Event** | A significant occurrence in the domain model (DDD) | `OrderPlaced`, `PaymentFailed`, `ShipmentDispatched` |
### EDA Topology
**Broker topology** (most common): Events flow through a central broker (Kafka, RabbitMQ, AWS EventBridge, Azure Service Bus).
```
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ Producer │───▶│ Event │───▶│ Consumer A │
│ │ │ Broker │───▶│ Consumer B │
│ │ │ (Kafka, │───▶│ Consumer C │
└──────────┘ │ RabbitMQ) │ └──────────────┘
└──────────────┘
```
**Mediator topology**: A central mediator orchestrates event flow (used when processing order matters).
### EDA Benefits and Tradeoffs
| Benefit | Tradeoff |
|---------|----------|
| Loose coupling between producers and consumers | Harder to trace and debug end-to-end flows |
| Independent scalability per component | Eventual consistency; no immediate confirmation |
| Temporal decoupling (producer doesn't wait) | Event ordering and deduplication challenges |
| Natural fit for audit trails | Schema evolution and versioning complexity |
| Easy to add new consumers without changing producers | Error handling is more complex (dead letters, retries) |
## Event Sourcing
Instead of storing the current state of an entity, store the **sequence of events** that led to the current state. The current state is derived by replaying the events.
### Traditional State vs. Event Sourcing
```
Traditional (State-based):
┌─────────────────────┐
│ Account │
│ balance: $750 │ ← Only current state; history lost
│ status: active │
└─────────────────────┘
Event Sourcing (Event Log):
┌─────────────────────────────────────────────────────┐
│ Event Store (Account #42) │
│ │
│ 1. AccountOpened { balance: $0 } 2024-01 │
│ 2. MoneyDeposited { amount: $1000 } 2024-01 │
│ 3. MoneyWithdrawn { amount: $200 } 2024-02 │
│ 4. MoneyWithdrawn { amount: $50 } 2024-03 │
│ │
│ Current state: replay events → balance: $750 │
└─────────────────────────────────────────────────────┘
```
### Event Store Structure
An event store is an append-only log organized by stream (typically one stream per aggregate):
| Column | Type | Description |
|--------|------|-------------|
| `stream_id` | string | Aggregate/entity identifier (e.g., `account-42`) |
| `event_id` | UUID | Unique event identifier |
| `event_type` | string | Event name (e.g., `MoneyDeposited`) |
| `data` | JSON/binary | Event payload |
| `metadata` | JSON | Correlation ID, causation ID, user, timestamp |
| `version` | integer | Sequence number within the stream (for optimistic concurrency) |
| `timestamp` | datetime | When the event was appended |
```sql
CREATE TABLE event_store (
stream_id VARCHAR(255) NOT NULL,
version INTEGER NOT NULL,
event_id UUID NOT NULL,
event_type VARCHAR(255) NOT NULL,
data JSONB NOT NULL,
metadata JSONB,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (stream_id, version)
);
```
### Event Store Implementations
| Technology | Type | Notes |
|-----------|------|-------|
| **EventStoreDB** | Purpose-built | Native event sourcing database; subscriptions, projections |
| **Marten** | .NET library | Event sourcing + document DB on top of PostgreSQL |
| **Axon Framework** | Java framework | Event sourcing + CQRS + saga support |
| **PostgreSQL + custom table** | DIY | Simple; use the schema above |
| **Apache Kafka** | Log-based | Can serve as event store; infinite retention + compaction |
| **DynamoDB + streams** | AWS | Single-table design with event streams |
### Event Sourcing Benefits
- **Complete audit trail** -- Every change is recorded as an immutable event.
- **Temporal queries** -- "What was the account balance on March 15?" Replay events up to that date.
- **Event replay** -- Rebuild read models, fix projections, or populate new services by replaying events.
- **Debugging** -- Reproduce any state by replaying the event sequence.
- **Domain richness** -- Events capture business intent, not just data changes.
### Event Sourcing Challenges
- **Event schema evolution** -- Events are immutable, but their schema must evolve. Use upcasting or versioned deserializers.
- **Replay performance** -- Long event streams are slow to replay. Use **snapshots** to checkpoint state periodically.
- **Eventual consistency** -- Read models (projections) are updated asynchronously; they may lag behind the write model.
- **Complexity** -- Significantly more complex than CRUD for simple domains.
- **Storage growth** -- Event stores grow continuously. Archiving and retention policies are needed.
## CQRS (Command Query Responsibility Segregation)
CQRS separates the model used for updating (commands/writes) from the model used for reading (queries/reads). This allows each side to be optimized independently.
### CQRS Architecture
```
┌──────────────────┐
│ Client │
└────┬────────┬────┘
│ │
Commands Queries
│ │
┌────▼──┐ ┌──▼──────┐
│ Write │ │ Read │
│ Model │ │ Model │
│ │ │ │
│Command│ │ Query │
│Handler│ │ Handler │
└───┬───┘ └────▲────┘
│ │
┌────▼───┐ ┌────┴────┐
│ Write │ │ Read │
│ Store │──▶│ Store │
│ │ │(Projec- │
└────────┘ │ tions) │
└─────────┘
```
### Why Separate Read and Write Models?
| Concern | Write Model | Read Model |
|---------|-------------|------------|
| **Optimization** | Normalized; optimized for consistency | Denormalized; optimized for query performance |
| **Scaling** | Scale for write throughput | Scale for read throughput (often 10-100x more reads) |
| **Schema** | Domain model (aggregates, entities) | Flat, query-specific views (projections) |
| **Validation** | Complex business rules, invariants | No business rules; just serving data |
| **Storage** | Event store, relational DB | Document DB, search index, cache, materialized views |
### Projections (Read Models)
Projections transform events into query-optimized read models. They subscribe to the event stream and update materialized views.
```
Event Stream:
OrderPlaced { orderId: 1, customer: "Alice", total: $50 }
OrderShipped { orderId: 1, trackingNumber: "XYZ123" }
Projection → Order Summary (Read Model):
{ orderId: 1, customer: "Alice", total: $50,
status: "Shipped", tracking: "XYZ123" }
```
Multiple projections can be built from the same event stream for different query needs:
- **Order summary** -- For the customer dashboard
- **Revenue report** -- For the finance team
- **Shipping manifest** -- For the warehouse
## Eventual Consistency
When using EDA, event sourcing, or CQRS, the system is eventually consistent -- updates propagate asynchronously and read models may temporarily be stale.
### Managing Eventual Consistency
| Strategy | Description |
|----------|-------------|
| **Causal consistency** | Ensure events are processed in causal order (use stream position / version) |
| **Read-your-own-writes** | After a command, query the write model directly (bypass read model) or wait for projection to catch up |
| **UI optimistic update** | Update the UI immediately; reconcile when the read model catches up |
| **Polling / subscription** | Client subscribes to updates or polls until the read model reflects the change |
| **Version stamping** | Include a version in responses; client retries if version is stale |
## Compensating Transactions
In eventually consistent systems, you cannot roll back distributed changes with a traditional transaction. Instead, use **compensating transactions** -- actions that semantically undo the effect of a previous action.
```
Forward: OrderPlaced → PaymentCharged → InventoryReserved → ShipmentCreated
Compensate: OrderCancelled ← PaymentRefunded ← InventoryReleased ← ShipmentCancelled
```
Compensating transactions are used in the saga pattern (see `dev/architecture/microservices` for choreography vs. orchestration).
## When to Use Each Pattern
| Pattern | Use When | Avoid When |
|---------|----------|------------|
| **EDA** | Multiple consumers need to react to changes; temporal decoupling needed; high scalability | Simple CRUD apps; strong consistency required; small systems with few components |
| **Event Sourcing** | Audit trail is critical; temporal queries needed; domain is event-centric; complex business rules | Simple CRUD domains; team unfamiliar with the pattern; high-volume writes with no audit need |
| **CQRS** | Read and write patterns differ significantly; need independent scaling; complex query requirements | Simple domains where reads and writes are symmetric; small systems; team unfamiliar with the pattern |
| **All three** | Complex domains with audit requirements, different read/write scaling needs, and reactive workflows | MVP or prototype; small team; simple domain; when any individual pattern would suffice |
## Best Practices
- Start with EDA alone if you only need loose coupling and reactive behavior. Add event sourcing or CQRS only when you have a specific need.
- Design events as first-class domain concepts: past-tense, immutable, carrying business intent (not CRUD operations).
- Plan for event schema evolution from day one. Use a schema registry (Avro, Protobuf) for strong contracts.
- Build projections to be rebuildable: if a projection is corrupted or needs to change, replay events from the beginning.
- Use snapshots for long-lived event streams to keep replay times reasonable.
- Handle idempotency in all event consumers: at-least-once delivery is the norm.
- Monitor projection lag (time between event publication and read model update) as a key operational metric.
- Keep the write model focused on enforcing business invariants; keep the read model focused on query performance.
architecture/event-driven/metadata.json
{
"version": "1.0.0",
"name": "event-driven",
"displayName": "Event-Driven Architecture",
"description": "Event-Driven Architecture (EDA), Event Sourcing, and CQRS -- complementary but independent patterns for building reactive, scalable systems with rich audit trails and temporal queries.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "Martin Fowler — Event Sourcing",
"url": "https://martinfowler.com/eaaDev/EventSourcing.html"
},
{
"title": "Martin Fowler — CQRS",
"url": "https://martinfowler.com/bliki/CQRS.html"
},
{
"title": "Martin Fowler — What Do You Mean by Event-Driven?",
"url": "https://martinfowler.com/articles/201701-event-driven.html"
}
]
}
architecture/event-driven/README.md
# Event-Driven Architecture
Event-Driven Architecture (EDA), Event Sourcing, and CQRS -- complementary but independent patterns for building reactive, scalable systems with rich audit trails and temporal queries.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 8 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/architecture/event-driven
```
## License
MIT
architecture/event-driven/rules/_sections.md
# Event-Driven Architecture Rules
Best practices and rules for Event-Driven Architecture.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Start with EDA alone if you only need loose coupling and... | MEDIUM | [`event-driven-start-with-eda-alone-if-you-only-need-loose-coupling-and.md`](event-driven-start-with-eda-alone-if-you-only-need-loose-coupling-and.md) |
| 2 | Design events as first-class domain concepts | MEDIUM | [`event-driven-design-events-as-first-class-domain-concepts.md`](event-driven-design-events-as-first-class-domain-concepts.md) |
| 3 | Plan for event schema evolution from day one | MEDIUM | [`event-driven-plan-for-event-schema-evolution-from-day-one.md`](event-driven-plan-for-event-schema-evolution-from-day-one.md) |
| 4 | Build projections to be rebuildable | MEDIUM | [`event-driven-build-projections-to-be-rebuildable.md`](event-driven-build-projections-to-be-rebuildable.md) |
| 5 | Use snapshots for long-lived event streams to keep replay... | MEDIUM | [`event-driven-use-snapshots-for-long-lived-event-streams-to-keep-replay.md`](event-driven-use-snapshots-for-long-lived-event-streams-to-keep-replay.md) |
| 6 | Handle idempotency in all event consumers | MEDIUM | [`event-driven-handle-idempotency-in-all-event-consumers.md`](event-driven-handle-idempotency-in-all-event-consumers.md) |
| 7 | Monitor projection lag (time between event publication and... | MEDIUM | [`event-driven-monitor-projection-lag-time-between-event-publication-and.md`](event-driven-monitor-projection-lag-time-between-event-publication-and.md) |
| 8 | Keep the write model focused on enforcing business... | MEDIUM | [`event-driven-keep-the-write-model-focused-on-enforcing-business.md`](event-driven-keep-the-write-model-focused-on-enforcing-business.md) |
architecture/event-driven/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: event-driven, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
architecture/event-driven/rules/event-driven-build-projections-to-be-rebuildable.md
---
title: "Build projections to be rebuildable"
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Build projections to be rebuildable
Build projections to be rebuildable: if a projection is corrupted or needs to change, replay events from the beginning.
architecture/event-driven/rules/event-driven-design-events-as-first-class-domain-concepts.md
---
title: "Design events as first-class domain concepts"
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Design events as first-class domain concepts
Design events as first-class domain concepts: past-tense, immutable, carrying business intent (not CRUD operations).
architecture/event-driven/rules/event-driven-handle-idempotency-in-all-event-consumers.md
---
title: "Handle idempotency in all event consumers"
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Handle idempotency in all event consumers
Handle idempotency in all event consumers: at-least-once delivery is the norm.
architecture/event-driven/rules/event-driven-keep-the-write-model-focused-on-enforcing-business.md
---
title: "Keep the write model focused on enforcing business..."
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Keep the write model focused on enforcing business...
Keep the write model focused on enforcing business invariants; keep the read model focused on query performance.
architecture/event-driven/rules/event-driven-monitor-projection-lag-time-between-event-publication-and.md
---
title: "Monitor projection lag (time between event publication and..."
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Monitor projection lag (time between event publication and...
Monitor projection lag (time between event publication and read model update) as a key operational metric.
architecture/event-driven/rules/event-driven-plan-for-event-schema-evolution-from-day-one.md
---
title: "Plan for event schema evolution from day one"
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Plan for event schema evolution from day one
Plan for event schema evolution from day one. Use a schema registry (Avro, Protobuf) for strong contracts.
architecture/event-driven/rules/event-driven-start-with-eda-alone-if-you-only-need-loose-coupling-and.md
---
title: "Start with EDA alone if you only need loose coupling and..."
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Start with EDA alone if you only need loose coupling and...
Start with EDA alone if you only need loose coupling and reactive behavior. Add event sourcing or CQRS only when you have a specific need.
architecture/event-driven/rules/event-driven-use-snapshots-for-long-lived-event-streams-to-keep-replay.md
---
title: "Use snapshots for long-lived event streams to keep replay..."
impact: MEDIUM
impactDescription: "general best practice"
tags: event-driven, dev, architecture, event-driven-architecture, event-sourcing, cqrs
---
## Use snapshots for long-lived event streams to keep replay...
Use snapshots for long-lived event streams to keep replay times reasonable.
architecture/event-driven/SKILL.md
---
name: event-driven
description: |
Event-Driven Architecture (EDA), Event Sourcing, and CQRS -- complementary but independent patterns for building reactive, scalable systems with rich audit trails and temporal queries.
USE FOR: event-driven architecture, event sourcing, CQRS, event stores, projections, eventual consistency, compensating transactions, temporal queries
DO NOT USE FOR: messaging channel patterns (use dev/integration-patterns), message routing (use dev/integration-patterns/message-routing), domain modeling (use domain-driven-design)
license: MIT
metadata:
displayName: "Event-Driven Architecture"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Martin Fowler — Event Sourcing"
url: "https://martinfowler.com/eaaDev/EventSourcing.html"
- title: "Martin Fowler — CQRS"
url: "https://martinfowler.com/bliki/CQRS.html"
- title: "Martin Fowler — What Do You Mean by Event-Driven?"
url: "https://martinfowler.com/articles/201701-event-driven.html"
---
# Event-Driven Architecture, Event Sourcing & CQRS
## Overview
This skill covers three complementary but independent patterns that are frequently used together:
1. **Event-Driven Architecture (EDA)** -- A system design where components communicate through events rather than direct calls.
2. **Event Sourcing** -- A persistence pattern where state is stored as a sequence of events rather than as current state.
3. **CQRS (Command Query Responsibility Segregation)** -- A pattern that separates read and write models.
These patterns can be used independently or combined. Understanding when to use each -- and when to combine them -- is critical.
## Canonical Works
| Book | Author(s) | Relevant Coverage |
|------|-----------|-------------------|
| *Designing Data-Intensive Applications* | Martin Kleppmann | Event sourcing, stream processing, change data capture |
| *Implementing Domain-Driven Design* | Vaughn Vernon | Domain events, event sourcing with DDD, CQRS |
| *Building Event-Driven Microservices* | Adam Bellemare | EDA at scale, event mesh, stream processing |
| *Enterprise Integration Patterns* | Hohpe & Woolf | Messaging foundations (see `dev/integration-patterns`) |
## Relationship Between the Three Patterns
```
┌─────────────────────────────────────────────────────────┐
│ │
│ Event-Driven Architecture (EDA) │
│ Components communicate through events │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Event Sourcing │ │ CQRS │ │
│ │ Store state as │ │ Separate read │ │
│ │ event log │ │ and write models │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ └───────────┬───────────┘ │
│ │ │
│ Can be combined but │
│ are independent patterns │
│ │
└─────────────────────────────────────────────────────────┘
```
- **EDA without Event Sourcing:** Services publish events but store current state in a traditional database.
- **Event Sourcing without CQRS:** Store state as events and rebuild current state from the event log -- but use the same model for reads and writes.
- **CQRS without Event Sourcing:** Separate read and write models backed by a traditional database.
- **All three combined:** The most powerful but most complex combination.
## Event-Driven Architecture (EDA)
In EDA, components produce and consume events. An event represents something that happened -- a fact. Events are immutable and past-tense (OrderPlaced, PaymentReceived, InventoryReserved).
### Event Types
| Type | Description | Example |
|------|-------------|---------|
| **Event Notification** | A thin signal that something happened; consumer fetches details if needed | `{ "type": "OrderPlaced", "orderId": "123" }` |
| **Event-Carried State Transfer** | Event carries the full state needed by consumers | `{ "type": "OrderPlaced", "orderId": "123", "items": [...], "total": 99.95 }` |
| **Domain Event** | A significant occurrence in the domain model (DDD) | `OrderPlaced`, `PaymentFailed`, `ShipmentDispatched` |
### EDA Topology
**Broker topology** (most common): Events flow through a central broker (Kafka, RabbitMQ, AWS EventBridge, Azure Service Bus).
```
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ Producer │───▶│ Event │───▶│ Consumer A │
│ │ │ Broker │───▶│ Consumer B │
│ │ │ (Kafka, │───▶│ Consumer C │
└──────────┘ │ RabbitMQ) │ └──────────────┘
└──────────────┘
```
**Mediator topology**: A central mediator orchestrates event flow (used when processing order matters).
### EDA Benefits and Tradeoffs
| Benefit | Tradeoff |
|---------|----------|
| Loose coupling between producers and consumers | Harder to trace and debug end-to-end flows |
| Independent scalability per component | Eventual consistency; no immediate confirmation |
| Temporal decoupling (producer doesn't wait) | Event ordering and deduplication challenges |
| Natural fit for audit trails | Schema evolution and versioning complexity |
| Easy to add new consumers without changing producers | Error handling is more complex (dead letters, retries) |
## Event Sourcing
Instead of storing the current state of an entity, store the **sequence of events** that led to the current state. The current state is derived by replaying the events.
### Traditional State vs. Event Sourcing
```
Traditional (State-based):
┌─────────────────────┐
│ Account │
│ balance: $750 │ ← Only current state; history lost
│ status: active │
└─────────────────────┘
Event Sourcing (Event Log):
┌─────────────────────────────────────────────────────┐
│ Event Store (Account #42) │
│ │
│ 1. AccountOpened { balance: $0 } 2024-01 │
│ 2. MoneyDeposited { amount: $1000 } 2024-01 │
│ 3. MoneyWithdrawn { amount: $200 } 2024-02 │
│ 4. MoneyWithdrawn { amount: $50 } 2024-03 │
│ │
│ Current state: replay events → balance: $750 │
└─────────────────────────────────────────────────────┘
```
### Event Store Structure
An event store is an append-only log organized by stream (typically one stream per aggregate):
| Column | Type | Description |
|--------|------|-------------|
| `stream_id` | string | Aggregate/entity identifier (e.g., `account-42`) |
| `event_id` | UUID | Unique event identifier |
| `event_type` | string | Event name (e.g., `MoneyDeposited`) |
| `data` | JSON/binary | Event payload |
| `metadata` | JSON | Correlation ID, causation ID, user, timestamp |
| `version` | integer | Sequence number within the stream (for optimistic concurrency) |
| `timestamp` | datetime | When the event was appended |
```sql
CREATE TABLE event_store (
stream_id VARCHAR(255) NOT NULL,
version INTEGER NOT NULL,
event_id UUID NOT NULL,
event_type VARCHAR(255) NOT NULL,
data JSONB NOT NULL,
metadata JSONB,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (stream_id, version)
);
```
### Event Store Implementations
| Technology | Type | Notes |
|-----------|------|-------|
| **EventStoreDB** | Purpose-built | Native event sourcing database; subscriptions, projections |
| **Marten** | .NET library | Event sourcing + document DB on top of PostgreSQL |
| **Axon Framework** | Java framework | Event sourcing + CQRS + saga support |
| **PostgreSQL + custom table** | DIY | Simple; use the schema above |
| **Apache Kafka** | Log-based | Can serve as event store; infinite retention + compaction |
| **DynamoDB + streams** | AWS | Single-table design with event streams |
### Event Sourcing Benefits
- **Complete audit trail** -- Every change is recorded as an immutable event.
- **Temporal queries** -- "What was the account balance on March 15?" Replay events up to that date.
- **Event replay** -- Rebuild read models, fix projections, or populate new services by replaying events.
- **Debugging** -- Reproduce any state by replaying the event sequence.
- **Domain richness** -- Events capture business intent, not just data changes.
### Event Sourcing Challenges
- **Event schema evolution** -- Events are immutable, but their schema must evolve. Use upcasting or versioned deserializers.
- **Replay performance** -- Long event streams are slow to replay. Use **snapshots** to checkpoint state periodically.
- **Eventual consistency** -- Read models (projections) are updated asynchronously; they may lag behind the write model.
- **Complexity** -- Significantly more complex than CRUD for simple domains.
- **Storage growth** -- Event stores grow continuously. Archiving and retention policies are needed.
## CQRS (Command Query Responsibility Segregation)
CQRS separates the model used for updating (commands/writes) from the model used for reading (queries/reads). This allows each side to be optimized independently.
### CQRS Architecture
```
┌──────────────────┐
│ Client │
└────┬────────┬────┘
│ │
Commands Queries
│ │
┌────▼──┐ ┌──▼──────┐
│ Write │ │ Read │
│ Model │ │ Model │
│ │ │ │
│Command│ │ Query │
│Handler│ │ Handler │
└───┬───┘ └────▲────┘
│ │
┌────▼───┐ ┌────┴────┐
│ Write │ │ Read │
│ Store │──▶│ Store │
│ │ │(Projec- │
└────────┘ │ tions) │
└─────────┘
```
### Why Separate Read and Write Models?
| Concern | Write Model | Read Model |
|---------|-------------|------------|
| **Optimization** | Normalized; optimized for consistency | Denormalized; optimized for query performance |
| **Scaling** | Scale for write throughput | Scale for read throughput (often 10-100x more reads) |
| **Schema** | Domain model (aggregates, entities) | Flat, query-specific views (projections) |
| **Validation** | Complex business rules, invariants | No business rules; just serving data |
| **Storage** | Event store, relational DB | Document DB, search index, cache, materialized views |
### Projections (Read Models)
Projections transform events into query-optimized read models. They subscribe to the event stream and update materialized views.
```
Event Stream:
OrderPlaced { orderId: 1, customer: "Alice", total: $50 }
OrderShipped { orderId: 1, trackingNumber: "XYZ123" }
Projection → Order Summary (Read Model):
{ orderId: 1, customer: "Alice", total: $50,
status: "Shipped", tracking: "XYZ123" }
```
Multiple projections can be built from the same event stream for different query needs:
- **Order summary** -- For the customer dashboard
- **Revenue report** -- For the finance team
- **Shipping manifest** -- For the warehouse
## Eventual Consistency
When using EDA, event sourcing, or CQRS, the system is eventually consistent -- updates propagate asynchronously and read models may temporarily be stale.
### Managing Eventual Consistency
| Strategy | Description |
|----------|-------------|
| **Causal consistency** | Ensure events are processed in causal order (use stream position / version) |
| **Read-your-own-writes** | After a command, query the write model directly (bypass read model) or wait for projection to catch up |
| **UI optimistic update** | Update the UI immediately; reconcile when the read model catches up |
| **Polling / subscription** | Client subscribes to updates or polls until the read model reflects the change |
| **Version stamping** | Include a version in responses; client retries if version is stale |
## Compensating Transactions
In eventually consistent systems, you cannot roll back distributed changes with a traditional transaction. Instead, use **compensating transactions** -- actions that semantically undo the effect of a previous action.
```
Forward: OrderPlaced → PaymentCharged → InventoryReserved → ShipmentCreated
Compensate: OrderCancelled ← PaymentRefunded ← InventoryReleased ← ShipmentCancelled
```
Compensating transactions are used in the saga pattern (see `dev/architecture/microservices` for choreography vs. orchestration).
## When to Use Each Pattern
| Pattern | Use When | Avoid When |
|---------|----------|------------|
| **EDA** | Multiple consumers need to react to changes; temporal decoupling needed; high scalability | Simple CRUD apps; strong consistency required; small systems with few components |
| **Event Sourcing** | Audit trail is critical; temporal queries needed; domain is event-centric; complex business rules | Simple CRUD domains; team unfamiliar with the pattern; high-volume writes with no audit need |
| **CQRS** | Read and write patterns differ significantly; need independent scaling; complex query requirements | Simple domains where reads and writes are symmetric; small systems; team unfamiliar with the pattern |
| **All three** | Complex domains with audit requirements, different read/write scaling needs, and reactive workflows | MVP or prototype; small team; simple domain; when any individual pattern would suffice |
## Best Practices
- Start with EDA alone if you only need loose coupling and reactive behavior. Add event sourcing or CQRS only when you have a specific need.
- Design events as first-class domain concepts: past-tense, immutable, carrying business intent (not CRUD operations).
- Plan for event schema evolution from day one. Use a schema registry (Avro, Protobuf) for strong contracts.
- Build projections to be rebuildable: if a projection is corrupted or needs to change, replay events from the beginning.
- Use snapshots for long-lived event streams to keep replay times reasonable.
- Handle idempotency in all event consumers: at-least-once delivery is the norm.
- Monitor projection lag (time between event publication and read model update) as a key operational metric.
- Keep the write model focused on enforcing business invariants; keep the read model focused on query performance.
architecture/hexagonal/AGENTS.md
# Hexagonal Architecture (Ports and Adapters)
## Overview
Hexagonal Architecture, introduced by Alistair Cockburn in 2005, organizes an application so that the core domain logic is isolated from external concerns (databases, APIs, UIs, message brokers) through **ports** (interfaces) and **adapters** (implementations). The goal is to make the application equally drivable by users, programs, automated tests, or batch scripts -- and equally connected to any external system.
The architecture is also known as **Ports and Adapters**. It shares the same fundamental principle as Onion Architecture (Jeffrey Palermo, 2008) and Clean Architecture (Robert C. Martin): **dependencies point inward; the domain depends on nothing external.**
## The Hexagonal Diagram
```
Driving Side (Primary)
(things that USE the app)
REST CLI Tests Events
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────┐
│ Primary Adapters │
│ (implement driving ports) │
│ │
┌─────┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤─────┐
│ │ Primary Ports │ │
│ │ (interfaces the app exposes) │ │
│ │ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Domain Model │ │ │
│ │ │ (Pure Business │ │ │
│ │ │ Logic) │ │ │
│ │ │ │ │ │
│ │ └───────────────────┘ │ │
│ │ │ │
│ │ Secondary Ports │ │
│ │ (interfaces the app needs) │ │
└─────┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤─────┘
│ Secondary Adapters │
│ (implement driven ports) │
└─────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
Postgres Redis Stripe Kafka
Driven Side (Secondary)
(things the app USES)
```
## Core Concepts
### Ports (Interfaces)
Ports define the boundaries of the application. They are **interfaces** -- contracts that describe what the application can do (primary/driving) or what the application needs (secondary/driven).
| Port Type | Also Called | Direction | Purpose | Example |
|-----------|------------|-----------|---------|---------|
| **Primary Port** | Driving Port | Inbound | Defines what the app **offers** to the outside world | `IOrderService.PlaceOrder(...)` |
| **Secondary Port** | Driven Port | Outbound | Defines what the app **requires** from the outside world | `IOrderRepository.Save(...)`, `IPaymentGateway.Charge(...)` |
```
// Primary port — what the application offers
public interface IOrderService
{
Task<OrderId> PlaceOrder(PlaceOrderCommand command);
Task<OrderDto> GetOrder(OrderId id);
Task CancelOrder(OrderId id);
}
// Secondary port — what the application needs
public interface IOrderRepository
{
Task<Order?> FindById(OrderId id);
Task Save(Order order);
}
// Secondary port — what the application needs
public interface IPaymentGateway
{
Task<PaymentResult> Charge(Money amount, PaymentMethod method);
Task Refund(PaymentId paymentId, Money amount);
}
```
### Adapters (Implementations)
Adapters are concrete implementations that connect ports to specific technologies. They live **outside** the domain core.
| Adapter Type | Also Called | Implements | Example |
|-------------|------------|------------|---------|
| **Primary Adapter** | Driving Adapter | Uses primary ports | REST controller, gRPC handler, CLI command, test harness |
| **Secondary Adapter** | Driven Adapter | Implements secondary ports | PostgreSQL repository, Stripe payment adapter, Kafka publisher |
```
// Primary adapter — REST controller drives the application
[ApiController]
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService; // Primary port
[HttpPost]
public async Task<IActionResult> PlaceOrder(PlaceOrderRequest request)
{
var command = MapToCommand(request);
var orderId = await _orderService.PlaceOrder(command);
return Created($"/orders/{orderId}", new { orderId });
}
}
// Secondary adapter — PostgreSQL implements the repository port
public class PostgresOrderRepository : IOrderRepository
{
private readonly DbContext _db;
public async Task<Order?> FindById(OrderId id)
{
return await _db.Orders
.Include(o => o.Lines)
.FirstOrDefaultAsync(o => o.Id == id);
}
public async Task Save(Order order) { ... }
}
// Secondary adapter — Stripe implements the payment port
public class StripePaymentGateway : IPaymentGateway
{
private readonly StripeClient _stripe;
public async Task<PaymentResult> Charge(Money amount, PaymentMethod method)
{
var intent = await _stripe.PaymentIntents.CreateAsync(...);
return MapToResult(intent);
}
}
```
### Driving vs. Driven Side
| Aspect | Driving (Primary) Side | Driven (Secondary) Side |
|--------|----------------------|------------------------|
| **Who initiates** | External actor drives the application | Application drives external systems |
| **Port direction** | Inbound (app receives calls) | Outbound (app makes calls) |
| **Adapter role** | Translates external input into domain calls | Translates domain calls into external system interactions |
| **Dependency direction** | Adapter depends on port (calls it) | Adapter implements port (the domain defines the interface) |
| **Examples** | HTTP controller, CLI, test, event consumer | Database, API client, message publisher, file system |
## Code Structure Example
```
src/
OrderService/
Domain/ # Pure domain model (no dependencies)
Order.cs
OrderLine.cs
Money.cs
OrderStatus.cs
Ports/
Primary/ # What the app offers
IOrderService.cs
Commands/
PlaceOrderCommand.cs
CancelOrderCommand.cs
Queries/
GetOrderQuery.cs
Secondary/ # What the app needs
IOrderRepository.cs
IPaymentGateway.cs
IInventoryClient.cs
IEventPublisher.cs
Application/ # Use case orchestration
OrderApplicationService.cs # Implements IOrderService
Adapters/
Primary/ # Driving adapters
Rest/
OrdersController.cs
Grpc/
OrderGrpcService.cs
Cli/
OrderCliCommand.cs
Secondary/ # Driven adapters
Persistence/
PostgresOrderRepository.cs
Payment/
StripePaymentGateway.cs
Messaging/
KafkaEventPublisher.cs
Composition/ # Wires everything together (DI)
ServiceRegistration.cs
```
## The Dependency Rule
The fundamental rule shared by Hexagonal, Onion, and Clean Architecture:
```
Dependencies point inward. Inner layers know nothing about outer layers.
┌─────────────────────────────────────────┐
│ Adapters (outermost) │
│ ┌─────────────────────────────────┐ │
│ │ Ports / Application │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Domain Model │ │ │
│ │ │ (innermost, no deps) │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
Outer depends on inner. Never the reverse.
```
- The **domain** has zero external dependencies. No framework imports, no database references, no HTTP concepts.
- **Ports** are defined by the domain/application layer using domain language.
- **Adapters** depend on ports (and on external libraries), never the reverse.
- **Composition root** (startup/DI configuration) wires adapters to ports.
## Comparison: Hexagonal vs. Onion vs. Clean Architecture
| Aspect | Hexagonal (Cockburn) | Onion (Palermo) | Clean (Martin) |
|--------|---------------------|-----------------|----------------|
| **Core idea** | Ports and Adapters | Concentric layers | Dependency Rule |
| **Visualization** | Hexagon with ports | Concentric circles | Concentric circles |
| **Inner layer** | Domain Model | Domain Model | Entities |
| **Boundary definition** | Ports (interfaces) | Layer interfaces | Use Case boundaries |
| **Outer layer** | Adapters | Infrastructure | Frameworks & Drivers |
| **Key emphasis** | Symmetry between driving/driven | Layer discipline | Use cases as central organizing concept |
| **Dependency direction** | Inward | Inward | Inward |
**They are the same fundamental idea expressed differently.** All three:
- Isolate the domain from infrastructure.
- Use interfaces (ports) at boundaries.
- Enforce the dependency rule: inner layers never reference outer layers.
- Enable technology swaps without changing business logic.
The practical differences are in emphasis and vocabulary, not in principle.
### Onion Architecture Layers (Palermo)
```
┌─────────────────────────────────────────┐
│ Infrastructure & UI (outermost) │
│ ┌─────────────────────────────────┐ │
│ │ Application Services │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Domain Services │ │ │
│ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ Domain Model │ │ │ │
│ │ │ │ (Entities, │ │ │ │
│ │ │ │ Value Objects)│ │ │ │
│ │ │ └─────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
## Testability: The Primary Benefit
The greatest practical benefit of hexagonal architecture is **testability**. Because the domain depends only on ports (interfaces), you can test business logic without any infrastructure.
```
// Test the domain using mock adapters — no database, no HTTP, no Stripe
[Test]
public async Task PlaceOrder_WithValidItems_ConfirmsOrder()
{
// Arrange — mock secondary ports
var orderRepo = new InMemoryOrderRepository();
var paymentGateway = new FakePaymentGateway(alwaysSucceeds: true);
var eventPublisher = new SpyEventPublisher();
// The application service uses ports, not concrete adapters
var service = new OrderApplicationService(
orderRepo, paymentGateway, eventPublisher);
// Act — drive through primary port
var orderId = await service.PlaceOrder(new PlaceOrderCommand
{
CustomerId = "cust-1",
Items = new[] { new OrderItem("prod-1", 2, 25.00m) }
});
// Assert — verify domain behavior
var order = await orderRepo.FindById(orderId);
Assert.Equal(OrderStatus.Confirmed, order.Status);
Assert.Single(eventPublisher.PublishedEvents
.OfType<OrderConfirmed>());
}
```
### Testing Strategy by Layer
| Layer | Test Type | What to Test | Infrastructure Needed |
|-------|-----------|-------------|----------------------|
| **Domain** | Unit tests | Business rules, invariants, calculations | None (pure logic) |
| **Application** | Unit tests with mocks | Use case orchestration, event publishing | Mock adapters |
| **Primary Adapters** | Integration tests | Request mapping, serialization, routing | HTTP test server |
| **Secondary Adapters** | Integration tests | Database queries, API calls, serialization | Real or containerized infrastructure |
| **Composition** | Smoke / E2E tests | Full system wiring, happy path | Full infrastructure |
## Common Mistakes
| Mistake | Problem | Fix |
|---------|---------|-----|
| **Domain imports framework** | Domain coupled to infrastructure; hard to test | Remove all framework dependencies from domain layer |
| **Adapter logic in domain** | Business logic leaks into controllers or repositories | Move logic to domain model or application service |
| **Port too broad** | Interface with 20 methods; hard to mock, violates ISP | Split into focused interfaces (Interface Segregation Principle) |
| **Skipping ports for "simplicity"** | Application calls database directly; loses swappability and testability | Always define a port even if you only have one adapter |
| **Anemic domain + fat service** | Domain model is just data; all logic in application service | Enrich the domain model with behavior (see `dev/architecture/domain-driven-design`) |
## Best Practices
- Keep the domain model completely free of infrastructure dependencies. No ORM attributes, no HTTP concepts, no serialization annotations in the domain layer.
- Define ports using domain language, not technology language. `IOrderRepository.Save(Order)`, not `IDatabaseContext.ExecuteCommand(SQL)`.
- Use dependency injection to wire adapters to ports at the composition root.
- Write the majority of tests against ports (mock adapters), not against infrastructure. This gives you fast, reliable tests.
- Start with one adapter per port. Add additional adapters when you actually need them (e.g., switching databases, adding a CLI interface).
- Use the hexagonal structure to enable incremental migration: swap one adapter at a time without touching the domain.
- Combine with DDD (see `dev/architecture/domain-driven-design`) for rich domain modeling inside the hexagon.
- The hexagonal shape is a metaphor for symmetry -- there is no inherent "top" or "bottom." Any adapter on any side is equally first-class.
architecture/hexagonal/metadata.json
{
"version": "1.0.0",
"name": "hexagonal",
"displayName": "Hexagonal Architecture",
"description": "Hexagonal Architecture (Ports and Adapters), Onion Architecture, and their relationship to Clean Architecture -- enabling technology-independent domain logic with high testability.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "Alistair Cockburn — Hexagonal Architecture (Ports and Adapters)",
"url": "https://alistair.cockburn.us/hexagonal-architecture/"
},
{
"title": "Hexagonal Architecture — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)"
}
]
}
architecture/hexagonal/README.md
# Hexagonal Architecture
Hexagonal Architecture (Ports and Adapters), Onion Architecture, and their relationship to Clean Architecture -- enabling technology-independent domain logic with high testability.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 8 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/architecture/hexagonal
```
## License
MIT
architecture/hexagonal/rules/_sections.md
# Hexagonal Architecture Rules
Best practices and rules for Hexagonal Architecture.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Keep the domain model completely free of infrastructure... | MEDIUM | [`hexagonal-keep-the-domain-model-completely-free-of-infrastructure.md`](hexagonal-keep-the-domain-model-completely-free-of-infrastructure.md) |
| 2 | Define ports using domain language, not technology language | MEDIUM | [`hexagonal-define-ports-using-domain-language-not-technology-language.md`](hexagonal-define-ports-using-domain-language-not-technology-language.md) |
| 3 | Use dependency injection to wire adapters to ports at the... | CRITICAL | [`hexagonal-use-dependency-injection-to-wire-adapters-to-ports-at-the.md`](hexagonal-use-dependency-injection-to-wire-adapters-to-ports-at-the.md) |
| 4 | Write the majority of tests against ports (mock adapters),... | MEDIUM | [`hexagonal-write-the-majority-of-tests-against-ports-mock-adapters.md`](hexagonal-write-the-majority-of-tests-against-ports-mock-adapters.md) |
| 5 | Start with one adapter per port | MEDIUM | [`hexagonal-start-with-one-adapter-per-port.md`](hexagonal-start-with-one-adapter-per-port.md) |
| 6 | Use the hexagonal structure to enable incremental migration | MEDIUM | [`hexagonal-use-the-hexagonal-structure-to-enable-incremental-migration.md`](hexagonal-use-the-hexagonal-structure-to-enable-incremental-migration.md) |
| 7 | Combine with DDD (see... | MEDIUM | [`hexagonal-combine-with-ddd-see.md`](hexagonal-combine-with-ddd-see.md) |
| 8 | The hexagonal shape is a metaphor for symmetry -- there is... | MEDIUM | [`hexagonal-the-hexagonal-shape-is-a-metaphor-for-symmetry-there-is.md`](hexagonal-the-hexagonal-shape-is-a-metaphor-for-symmetry-there-is.md) |
architecture/hexagonal/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: hexagonal, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
architecture/hexagonal/rules/hexagonal-combine-with-ddd-see.md
---
title: "Combine with DDD (see..."
impact: MEDIUM
impactDescription: "general best practice"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## Combine with DDD (see...
Combine with DDD (see `dev/architecture/domain-driven-design`) for rich domain modeling inside the hexagon.
architecture/hexagonal/rules/hexagonal-define-ports-using-domain-language-not-technology-language.md
---
title: "Define ports using domain language, not technology language"
impact: MEDIUM
impactDescription: "general best practice"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## Define ports using domain language, not technology language
Define ports using domain language, not technology language. `IOrderRepository.Save(Order)`, not `IDatabaseContext.ExecuteCommand(SQL)`.
architecture/hexagonal/rules/hexagonal-keep-the-domain-model-completely-free-of-infrastructure.md
---
title: "Keep the domain model completely free of infrastructure..."
impact: MEDIUM
impactDescription: "general best practice"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## Keep the domain model completely free of infrastructure...
Keep the domain model completely free of infrastructure dependencies. No ORM attributes, no HTTP concepts, no serialization annotations in the domain layer.
architecture/hexagonal/rules/hexagonal-start-with-one-adapter-per-port.md
---
title: "Start with one adapter per port"
impact: MEDIUM
impactDescription: "general best practice"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## Start with one adapter per port
Start with one adapter per port. Add additional adapters when you actually need them (e.g., switching databases, adding a CLI interface).
architecture/hexagonal/rules/hexagonal-the-hexagonal-shape-is-a-metaphor-for-symmetry-there-is.md
---
title: "The hexagonal shape is a metaphor for symmetry -- there is..."
impact: MEDIUM
impactDescription: "general best practice"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## The hexagonal shape is a metaphor for symmetry -- there is...
The hexagonal shape is a metaphor for symmetry -- there is no inherent "top" or "bottom." Any adapter on any side is equally first-class.
architecture/hexagonal/rules/hexagonal-use-dependency-injection-to-wire-adapters-to-ports-at-the.md
---
title: "Use dependency injection to wire adapters to ports at the..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## Use dependency injection to wire adapters to ports at the...
Use dependency injection to wire adapters to ports at the composition root.
architecture/hexagonal/rules/hexagonal-use-the-hexagonal-structure-to-enable-incremental-migration.md
---
title: "Use the hexagonal structure to enable incremental migration"
impact: MEDIUM
impactDescription: "general best practice"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## Use the hexagonal structure to enable incremental migration
Use the hexagonal structure to enable incremental migration: swap one adapter at a time without touching the domain.
architecture/hexagonal/rules/hexagonal-write-the-majority-of-tests-against-ports-mock-adapters.md
---
title: "Write the majority of tests against ports (mock adapters),..."
impact: MEDIUM
impactDescription: "general best practice"
tags: hexagonal, dev, architecture, hexagonal-architecture, ports-and-adapters, onion-architecture
---
## Write the majority of tests against ports (mock adapters),...
Write the majority of tests against ports (mock adapters), not against infrastructure. This gives you fast, reliable tests.
architecture/hexagonal/SKILL.md
---
name: hexagonal
description: |
Hexagonal Architecture (Ports and Adapters), Onion Architecture, and their relationship to Clean Architecture -- enabling technology-independent domain logic with high testability.
USE FOR: hexagonal architecture, ports and adapters, onion architecture, driving/driven adapters, technology-independent domain design, adapter-based testability
DO NOT USE FOR: clean architecture layers specifically (use dev/craftsmanship/clean-architecture), microservice boundaries (use microservices), domain model design (use domain-driven-design)
license: MIT
metadata:
displayName: "Hexagonal Architecture"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Alistair Cockburn — Hexagonal Architecture (Ports and Adapters)"
url: "https://alistair.cockburn.us/hexagonal-architecture/"
- title: "Hexagonal Architecture — Wikipedia"
url: "https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)"
---
# Hexagonal Architecture (Ports and Adapters)
## Overview
Hexagonal Architecture, introduced by Alistair Cockburn in 2005, organizes an application so that the core domain logic is isolated from external concerns (databases, APIs, UIs, message brokers) through **ports** (interfaces) and **adapters** (implementations). The goal is to make the application equally drivable by users, programs, automated tests, or batch scripts -- and equally connected to any external system.
The architecture is also known as **Ports and Adapters**. It shares the same fundamental principle as Onion Architecture (Jeffrey Palermo, 2008) and Clean Architecture (Robert C. Martin): **dependencies point inward; the domain depends on nothing external.**
## The Hexagonal Diagram
```
Driving Side (Primary)
(things that USE the app)
REST CLI Tests Events
│ │ │ │
▼ ▼ ▼ ▼
┌─────────────────────────────────┐
│ Primary Adapters │
│ (implement driving ports) │
│ │
┌─────┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤─────┐
│ │ Primary Ports │ │
│ │ (interfaces the app exposes) │ │
│ │ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ │ │ │
│ │ │ Domain Model │ │ │
│ │ │ (Pure Business │ │ │
│ │ │ Logic) │ │ │
│ │ │ │ │ │
│ │ └───────────────────┘ │ │
│ │ │ │
│ │ Secondary Ports │ │
│ │ (interfaces the app needs) │ │
└─────┤─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─┤─────┘
│ Secondary Adapters │
│ (implement driven ports) │
└─────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
Postgres Redis Stripe Kafka
Driven Side (Secondary)
(things the app USES)
```
## Core Concepts
### Ports (Interfaces)
Ports define the boundaries of the application. They are **interfaces** -- contracts that describe what the application can do (primary/driving) or what the application needs (secondary/driven).
| Port Type | Also Called | Direction | Purpose | Example |
|-----------|------------|-----------|---------|---------|
| **Primary Port** | Driving Port | Inbound | Defines what the app **offers** to the outside world | `IOrderService.PlaceOrder(...)` |
| **Secondary Port** | Driven Port | Outbound | Defines what the app **requires** from the outside world | `IOrderRepository.Save(...)`, `IPaymentGateway.Charge(...)` |
```
// Primary port — what the application offers
public interface IOrderService
{
Task<OrderId> PlaceOrder(PlaceOrderCommand command);
Task<OrderDto> GetOrder(OrderId id);
Task CancelOrder(OrderId id);
}
// Secondary port — what the application needs
public interface IOrderRepository
{
Task<Order?> FindById(OrderId id);
Task Save(Order order);
}
// Secondary port — what the application needs
public interface IPaymentGateway
{
Task<PaymentResult> Charge(Money amount, PaymentMethod method);
Task Refund(PaymentId paymentId, Money amount);
}
```
### Adapters (Implementations)
Adapters are concrete implementations that connect ports to specific technologies. They live **outside** the domain core.
| Adapter Type | Also Called | Implements | Example |
|-------------|------------|------------|---------|
| **Primary Adapter** | Driving Adapter | Uses primary ports | REST controller, gRPC handler, CLI command, test harness |
| **Secondary Adapter** | Driven Adapter | Implements secondary ports | PostgreSQL repository, Stripe payment adapter, Kafka publisher |
```
// Primary adapter — REST controller drives the application
[ApiController]
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService; // Primary port
[HttpPost]
public async Task<IActionResult> PlaceOrder(PlaceOrderRequest request)
{
var command = MapToCommand(request);
var orderId = await _orderService.PlaceOrder(command);
return Created($"/orders/{orderId}", new { orderId });
}
}
// Secondary adapter — PostgreSQL implements the repository port
public class PostgresOrderRepository : IOrderRepository
{
private readonly DbContext _db;
public async Task<Order?> FindById(OrderId id)
{
return await _db.Orders
.Include(o => o.Lines)
.FirstOrDefaultAsync(o => o.Id == id);
}
public async Task Save(Order order) { ... }
}
// Secondary adapter — Stripe implements the payment port
public class StripePaymentGateway : IPaymentGateway
{
private readonly StripeClient _stripe;
public async Task<PaymentResult> Charge(Money amount, PaymentMethod method)
{
var intent = await _stripe.PaymentIntents.CreateAsync(...);
return MapToResult(intent);
}
}
```
### Driving vs. Driven Side
| Aspect | Driving (Primary) Side | Driven (Secondary) Side |
|--------|----------------------|------------------------|
| **Who initiates** | External actor drives the application | Application drives external systems |
| **Port direction** | Inbound (app receives calls) | Outbound (app makes calls) |
| **Adapter role** | Translates external input into domain calls | Translates domain calls into external system interactions |
| **Dependency direction** | Adapter depends on port (calls it) | Adapter implements port (the domain defines the interface) |
| **Examples** | HTTP controller, CLI, test, event consumer | Database, API client, message publisher, file system |
## Code Structure Example
```
src/
OrderService/
Domain/ # Pure domain model (no dependencies)
Order.cs
OrderLine.cs
Money.cs
OrderStatus.cs
Ports/
Primary/ # What the app offers
IOrderService.cs
Commands/
PlaceOrderCommand.cs
CancelOrderCommand.cs
Queries/
GetOrderQuery.cs
Secondary/ # What the app needs
IOrderRepository.cs
IPaymentGateway.cs
IInventoryClient.cs
IEventPublisher.cs
Application/ # Use case orchestration
OrderApplicationService.cs # Implements IOrderService
Adapters/
Primary/ # Driving adapters
Rest/
OrdersController.cs
Grpc/
OrderGrpcService.cs
Cli/
OrderCliCommand.cs
Secondary/ # Driven adapters
Persistence/
PostgresOrderRepository.cs
Payment/
StripePaymentGateway.cs
Messaging/
KafkaEventPublisher.cs
Composition/ # Wires everything together (DI)
ServiceRegistration.cs
```
## The Dependency Rule
The fundamental rule shared by Hexagonal, Onion, and Clean Architecture:
```
Dependencies point inward. Inner layers know nothing about outer layers.
┌─────────────────────────────────────────┐
│ Adapters (outermost) │
│ ┌─────────────────────────────────┐ │
│ │ Ports / Application │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Domain Model │ │ │
│ │ │ (innermost, no deps) │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
Outer depends on inner. Never the reverse.
```
- The **domain** has zero external dependencies. No framework imports, no database references, no HTTP concepts.
- **Ports** are defined by the domain/application layer using domain language.
- **Adapters** depend on ports (and on external libraries), never the reverse.
- **Composition root** (startup/DI configuration) wires adapters to ports.
## Comparison: Hexagonal vs. Onion vs. Clean Architecture
| Aspect | Hexagonal (Cockburn) | Onion (Palermo) | Clean (Martin) |
|--------|---------------------|-----------------|----------------|
| **Core idea** | Ports and Adapters | Concentric layers | Dependency Rule |
| **Visualization** | Hexagon with ports | Concentric circles | Concentric circles |
| **Inner layer** | Domain Model | Domain Model | Entities |
| **Boundary definition** | Ports (interfaces) | Layer interfaces | Use Case boundaries |
| **Outer layer** | Adapters | Infrastructure | Frameworks & Drivers |
| **Key emphasis** | Symmetry between driving/driven | Layer discipline | Use cases as central organizing concept |
| **Dependency direction** | Inward | Inward | Inward |
**They are the same fundamental idea expressed differently.** All three:
- Isolate the domain from infrastructure.
- Use interfaces (ports) at boundaries.
- Enforce the dependency rule: inner layers never reference outer layers.
- Enable technology swaps without changing business logic.
The practical differences are in emphasis and vocabulary, not in principle.
### Onion Architecture Layers (Palermo)
```
┌─────────────────────────────────────────┐
│ Infrastructure & UI (outermost) │
│ ┌─────────────────────────────────┐ │
│ │ Application Services │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Domain Services │ │ │
│ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ Domain Model │ │ │ │
│ │ │ │ (Entities, │ │ │ │
│ │ │ │ Value Objects)│ │ │ │
│ │ │ └─────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘
```
## Testability: The Primary Benefit
The greatest practical benefit of hexagonal architecture is **testability**. Because the domain depends only on ports (interfaces), you can test business logic without any infrastructure.
```
// Test the domain using mock adapters — no database, no HTTP, no Stripe
[Test]
public async Task PlaceOrder_WithValidItems_ConfirmsOrder()
{
// Arrange — mock secondary ports
var orderRepo = new InMemoryOrderRepository();
var paymentGateway = new FakePaymentGateway(alwaysSucceeds: true);
var eventPublisher = new SpyEventPublisher();
// The application service uses ports, not concrete adapters
var service = new OrderApplicationService(
orderRepo, paymentGateway, eventPublisher);
// Act — drive through primary port
var orderId = await service.PlaceOrder(new PlaceOrderCommand
{
CustomerId = "cust-1",
Items = new[] { new OrderItem("prod-1", 2, 25.00m) }
});
// Assert — verify domain behavior
var order = await orderRepo.FindById(orderId);
Assert.Equal(OrderStatus.Confirmed, order.Status);
Assert.Single(eventPublisher.PublishedEvents
.OfType<OrderConfirmed>());
}
```
### Testing Strategy by Layer
| Layer | Test Type | What to Test | Infrastructure Needed |
|-------|-----------|-------------|----------------------|
| **Domain** | Unit tests | Business rules, invariants, calculations | None (pure logic) |
| **Application** | Unit tests with mocks | Use case orchestration, event publishing | Mock adapters |
| **Primary Adapters** | Integration tests | Request mapping, serialization, routing | HTTP test server |
| **Secondary Adapters** | Integration tests | Database queries, API calls, serialization | Real or containerized infrastructure |
| **Composition** | Smoke / E2E tests | Full system wiring, happy path | Full infrastructure |
## Common Mistakes
| Mistake | Problem | Fix |
|---------|---------|-----|
| **Domain imports framework** | Domain coupled to infrastructure; hard to test | Remove all framework dependencies from domain layer |
| **Adapter logic in domain** | Business logic leaks into controllers or repositories | Move logic to domain model or application service |
| **Port too broad** | Interface with 20 methods; hard to mock, violates ISP | Split into focused interfaces (Interface Segregation Principle) |
| **Skipping ports for "simplicity"** | Application calls database directly; loses swappability and testability | Always define a port even if you only have one adapter |
| **Anemic domain + fat service** | Domain model is just data; all logic in application service | Enrich the domain model with behavior (see `dev/architecture/domain-driven-design`) |
## Best Practices
- Keep the domain model completely free of infrastructure dependencies. No ORM attributes, no HTTP concepts, no serialization annotations in the domain layer.
- Define ports using domain language, not technology language. `IOrderRepository.Save(Order)`, not `IDatabaseContext.ExecuteCommand(SQL)`.
- Use dependency injection to wire adapters to ports at the composition root.
- Write the majority of tests against ports (mock adapters), not against infrastructure. This gives you fast, reliable tests.
- Start with one adapter per port. Add additional adapters when you actually need them (e.g., switching databases, adding a CLI interface).
- Use the hexagonal structure to enable incremental migration: swap one adapter at a time without touching the domain.
- Combine with DDD (see `dev/architecture/domain-driven-design`) for rich domain modeling inside the hexagon.
- The hexagonal shape is a metaphor for symmetry -- there is no inherent "top" or "bottom." Any adapter on any side is equally first-class.
architecture/metadata.json
{
"version": "1.0.0",
"name": "architecture",
"displayName": "Architecture",
"description": "Use when selecting architecture styles, evaluating system decomposition strategies, or analyzing architecture characteristics (quality attributes) for a system.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "Martin Fowler — Software Architecture Guide",
"url": "https://martinfowler.com/architecture/"
},
{
"title": "Software Architecture — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Software_architecture"
}
]
}
architecture/microservices/AGENTS.md
# Microservices Architecture
## Overview
Microservices architecture structures an application as a collection of loosely coupled, independently deployable services, each organized around a business capability. Each service owns its data, runs in its own process, and communicates over the network.
The canonical reference is Sam Newman's *Building Microservices* (O'Reilly, 2nd edition 2021), supplemented by *Monolith to Microservices* (Newman, 2019) for migration strategies.
**The First Rule of Microservices:** Don't start with microservices. Start with a monolith, understand your domain, and decompose when you have evidence that the benefits outweigh the operational costs. (See `dev/architecture/monoliths` for the monolith-first approach.)
## Service Decomposition
### By Business Capability
Align services to what the business does (e.g., Order Management, Inventory, Payments). This creates stable boundaries because business capabilities change less frequently than technical layers.
### By Subdomain (DDD-Aligned)
Use Domain-Driven Design bounded contexts as service boundaries (see `dev/architecture/domain-driven-design`):
- **Core subdomains** -- Your competitive advantage; build custom services.
- **Supporting subdomains** -- Necessary but not differentiating; simpler services or libraries.
- **Generic subdomains** -- Commodity; buy or use off-the-shelf (auth, email, payments).
### Decomposition Heuristics
| Heuristic | Description |
|-----------|-------------|
| **Single Responsibility** | Each service does one thing well |
| **Data ownership** | Each service owns its data; no shared databases |
| **Independent deployability** | Changing one service does not require deploying another |
| **Team alignment** | One team can own and operate the service end-to-end |
| **Bounded context boundary** | Service boundaries align with DDD bounded contexts |
## Inter-Service Communication
### Synchronous Communication
| Pattern | Protocol | When to Use |
|---------|----------|-------------|
| **Request/Response (REST)** | HTTP/JSON | Simple CRUD, external APIs, broad tooling support |
| **Request/Response (gRPC)** | HTTP/2 + Protobuf | Internal service-to-service; high throughput, strong typing, streaming |
| **GraphQL** | HTTP/JSON | Client-driven queries; aggregating multiple services for a frontend |
### Asynchronous Communication
| Pattern | Mechanism | When to Use |
|---------|-----------|-------------|
| **Event Notification** | Message broker (topic/pub-sub) | Decoupled notification; consumers decide what to do |
| **Event-Carried State Transfer** | Message broker with payload | Reduce synchronous callbacks; consumer has needed data |
| **Command Message** | Message broker (queue) | Tell a specific service to do something |
| **Async Request/Response** | Correlation ID + reply queue | Need a response but don't want to block |
**Rule of thumb:** Prefer asynchronous communication for inter-service calls. Use synchronous only when a real-time response is required (e.g., user-facing request/response).
### Communication Anti-Patterns
- **Distributed monolith** -- Services are "microservices" in name only; they deploy together, share databases, or cannot function independently.
- **Chatty interfaces** -- Excessive synchronous calls between services creating latency chains.
- **Shared database** -- Multiple services reading/writing the same tables destroys independent deployability.
## API Gateway
An API gateway sits between external clients and internal services, providing:
- **Request routing** -- Routes client requests to the appropriate microservice
- **Protocol translation** -- External REST to internal gRPC, for example
- **Authentication/Authorization** -- Centralized security enforcement
- **Rate limiting and throttling** -- Protect services from traffic spikes
- **Response aggregation** -- Combine responses from multiple services for a single client call
Common implementations: Kong, AWS API Gateway, Azure API Management, Envoy, NGINX, Ocelot (.NET).
## Service Mesh
A service mesh handles service-to-service networking concerns transparently via sidecar proxies:
```
┌──────────────────────┐ ┌──────────────────────┐
│ Service A │ │ Service B │
│ ┌────────────────┐ │ │ ┌────────────────┐ │
│ │ App Container │ │ │ │ App Container │ │
│ └───────┬────────┘ │ │ └───────▲────────┘ │
│ │ │ │ │ │
│ ┌───────▼────────┐ │ │ ┌───────┴────────┐ │
│ │ Sidecar Proxy │──┼────┼─▶│ Sidecar Proxy │ │
│ │ (Envoy) │ │ │ │ (Envoy) │ │
│ └────────────────┘ │ │ └────────────────┘ │
└──────────────────────┘ └──────────────────────┘
Control Plane (Istio / Linkerd)
```
**Capabilities:** Mutual TLS, traffic management, retries, circuit breaking, observability (distributed tracing, metrics), canary deployments.
**Implementations:** Istio, Linkerd, Consul Connect, AWS App Mesh.
## Saga Pattern -- Distributed Transactions
Since each microservice owns its data, distributed transactions (2PC) are impractical. The saga pattern manages data consistency across services through a sequence of local transactions with compensating actions.
### Choreography (Event-Driven)
Each service publishes events that trigger the next step. No central coordinator.
```
Order Service ──(OrderCreated)──▶ Payment Service
Payment Service ──(PaymentProcessed)──▶ Inventory Service
Inventory Service ──(InventoryReserved)──▶ Shipping Service
On failure:
Inventory Service ──(ReservationFailed)──▶ Payment Service (refund)
Payment Service ──(RefundProcessed)──▶ Order Service (cancel)
```
**Pros:** Simple, decoupled, no single point of failure.
**Cons:** Hard to understand the overall flow; debugging is difficult; risk of cyclic dependencies.
### Orchestration (Central Coordinator)
A saga orchestrator (process manager) coordinates the steps explicitly.
```
┌─────────────────┐
│ Saga Orchestrator│
│ (Order Saga) │
└────┬───┬───┬────┘
│ │ │
▼ ▼ ▼
Payment Inventory Shipping
Service Service Service
```
**Pros:** Clear flow, easier to understand and debug, centralized compensation logic.
**Cons:** Orchestrator is a coupling point; risk of becoming a "god service."
**Guidance:** Use choreography for simple sagas (2-3 steps). Use orchestration for complex flows (4+ steps or complex compensation).
## Distributed Data Management
| Pattern | Description |
|---------|-------------|
| **Database per Service** | Each service has its own database; no shared access |
| **API Composition** | Query multiple services and aggregate results |
| **CQRS** | Separate read and write models for different optimization (see `dev/architecture/event-driven`) |
| **Event Sourcing** | Store state changes as events; derive current state (see `dev/architecture/event-driven`) |
| **Saga** | Manage distributed transactions through compensating actions |
| **Outbox Pattern** | Reliably publish events by writing to a local outbox table within the same transaction |
## Service Discovery
Services need to find each other in a dynamic environment where instances come and go.
| Approach | Examples | Mechanism |
|----------|----------|-----------|
| **Client-side discovery** | Netflix Eureka, Consul | Client queries registry, picks instance |
| **Server-side discovery** | AWS ALB, Kubernetes Services | Load balancer/proxy routes to available instance |
| **DNS-based** | Consul DNS, Kubernetes CoreDNS | Resolve service name to IP(s) via DNS |
In Kubernetes environments, server-side discovery via Services and DNS is the default and usually sufficient.
## Resilience Patterns
| Pattern | Purpose |
|---------|---------|
| **Circuit Breaker** | Stop calling a failing service; fail fast and allow recovery |
| **Retry with Backoff** | Retry transient failures with exponential backoff and jitter |
| **Bulkhead** | Isolate failures to prevent cascading (separate thread pools / connections) |
| **Timeout** | Set explicit timeouts on all remote calls; never wait forever |
| **Fallback** | Provide degraded but functional response when a service is unavailable |
| **Health Check** | Expose liveness and readiness endpoints for orchestrators |
## When NOT to Use Microservices
Microservices introduce significant operational complexity. Do not use them when:
- **Your team is small** (< 8-10 developers) -- The overhead exceeds the benefit.
- **Your domain is not well understood** -- You will draw the wrong boundaries and create a distributed monolith.
- **You lack operational maturity** -- You need CI/CD, monitoring, distributed tracing, container orchestration, and on-call practices before microservices are viable.
- **Latency is critical** -- Every network hop adds latency; monoliths have zero network overhead for internal calls.
- **Strong consistency is required everywhere** -- Microservices embrace eventual consistency; if your domain requires ACID transactions across multiple entities, a monolith may be simpler.
- **You are building an MVP or prototype** -- Speed of iteration matters more than scalability at this stage.
## Tradeoffs Summary
| Benefit | Cost |
|---------|------|
| Independent deployability | Operational complexity (CI/CD per service, monitoring, tracing) |
| Technology heterogeneity | Polyglot overhead; harder to maintain standards |
| Team autonomy | Coordination overhead; contract management |
| Scalability per service | Network latency; serialization/deserialization cost |
| Fault isolation | Distributed failure modes (partial failures, network partitions) |
| Organizational alignment | Requires mature DevOps culture |
## Best Practices
- Design for failure from day one: circuit breakers, retries, timeouts, bulkheads.
- Own your data: one database per service, no shared database access.
- Make inter-service communication observable: distributed tracing (OpenTelemetry), centralized logging, metrics.
- Use consumer-driven contract testing (Pact, Spring Cloud Contract) to prevent breaking changes.
- Prefer asynchronous communication; use synchronous calls only when necessary.
- Keep services small enough to be owned by a single team, but large enough to justify the operational overhead.
- Deploy independently, test independently, fail independently.
architecture/microservices/metadata.json
{
"version": "1.0.0",
"name": "microservices",
"displayName": "Microservices",
"description": "Microservice architecture patterns and practices based on Sam Newman's \"Building Microservices\" -- covering service decomposition, inter-service communication, data management, and operational patterns.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "Martin Fowler — Microservices Guide",
"url": "https://martinfowler.com/microservices/"
},
{
"title": "Microservices — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Microservices"
}
]
}
architecture/microservices/README.md
# Microservices
Microservice architecture patterns and practices based on Sam Newman's "Building Microservices" -- covering service decomposition, inter-service communication, data management, and operational patterns.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 7 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/architecture/microservices
```
## License
MIT
architecture/microservices/rules/_sections.md
# Microservices Rules
Best practices and rules for Microservices.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Design for failure from day one | MEDIUM | [`microservices-design-for-failure-from-day-one.md`](microservices-design-for-failure-from-day-one.md) |
| 2 | Own your data | MEDIUM | [`microservices-own-your-data.md`](microservices-own-your-data.md) |
| 3 | Make inter-service communication observable | MEDIUM | [`microservices-make-inter-service-communication-observable.md`](microservices-make-inter-service-communication-observable.md) |
| 4 | Use consumer-driven contract testing (Pact, Spring Cloud... | HIGH | [`microservices-use-consumer-driven-contract-testing-pact-spring-cloud.md`](microservices-use-consumer-driven-contract-testing-pact-spring-cloud.md) |
| 5 | Prefer asynchronous communication; use synchronous calls... | LOW | [`microservices-prefer-asynchronous-communication-use-synchronous-calls.md`](microservices-prefer-asynchronous-communication-use-synchronous-calls.md) |
| 6 | Keep services small enough to be owned by a single team,... | MEDIUM | [`microservices-keep-services-small-enough-to-be-owned-by-a-single-team.md`](microservices-keep-services-small-enough-to-be-owned-by-a-single-team.md) |
| 7 | Deploy independently, test independently, fail independently | MEDIUM | [`microservices-deploy-independently-test-independently-fail-independently.md`](microservices-deploy-independently-test-independently-fail-independently.md) |
architecture/microservices/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: microservices, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
architecture/microservices/rules/microservices-deploy-independently-test-independently-fail-independently.md
---
title: "Deploy independently, test independently, fail independently"
impact: MEDIUM
impactDescription: "general best practice"
tags: microservices, dev, architecture, microservice-decomposition, inter-service-communication, service-mesh
---
## Deploy independently, test independently, fail independently
Deploy independently, test independently, fail independently.
architecture/microservices/rules/microservices-design-for-failure-from-day-one.md
---
title: "Design for failure from day one"
impact: MEDIUM
impactDescription: "general best practice"
tags: microservices, dev, architecture, microservice-decomposition, inter-service-communication, service-mesh
---
## Design for failure from day one
Design for failure from day one: circuit breakers, retries, timeouts, bulkheads.
architecture/microservices/rules/microservices-keep-services-small-enough-to-be-owned-by-a-single-team.md
---
title: "Keep services small enough to be owned by a single team,..."
impact: MEDIUM
impactDescription: "general best practice"
tags: microservices, dev, architecture, microservice-decomposition, inter-service-communication, service-mesh
---
## Keep services small enough to be owned by a single team,...
Keep services small enough to be owned by a single team, but large enough to justify the operational overhead.
architecture/microservices/rules/microservices-make-inter-service-communication-observable.md
---
title: "Make inter-service communication observable"
impact: MEDIUM
impactDescription: "general best practice"
tags: microservices, dev, architecture, microservice-decomposition, inter-service-communication, service-mesh
---
## Make inter-service communication observable
Make inter-service communication observable: distributed tracing (OpenTelemetry), centralized logging, metrics.
architecture/microservices/rules/microservices-own-your-data.md
---
title: "Own your data"
impact: MEDIUM
impactDescription: "general best practice"
tags: microservices, dev, architecture, microservice-decomposition, inter-service-communication, service-mesh
---
## Own your data
Own your data: one database per service, no shared database access.
architecture/microservices/rules/microservices-prefer-asynchronous-communication-use-synchronous-calls.md
---
title: "Prefer asynchronous communication; use synchronous calls..."
impact: LOW
impactDescription: "recommended but situational"
tags: microservices, dev, architecture, microservice-decomposition, inter-service-communication, service-mesh
---
## Prefer asynchronous communication; use synchronous calls...
Prefer asynchronous communication; use synchronous calls only when necessary.
architecture/microservices/rules/microservices-use-consumer-driven-contract-testing-pact-spring-cloud.md
---
title: "Use consumer-driven contract testing (Pact, Spring Cloud..."
impact: HIGH
impactDescription: "significant quality or reliability improvement"
tags: microservices, dev, architecture, microservice-decomposition, inter-service-communication, service-mesh
---
## Use consumer-driven contract testing (Pact, Spring Cloud...
Use consumer-driven contract testing (Pact, Spring Cloud Contract) to prevent breaking changes.
architecture/microservices/SKILL.md
---
name: microservices
description: |
Microservice architecture patterns and practices based on Sam Newman's "Building Microservices" -- covering service decomposition, inter-service communication, data management, and operational patterns.
USE FOR: microservice decomposition, inter-service communication, service mesh, API gateway, saga pattern, service discovery, distributed data management
DO NOT USE FOR: monolithic architecture (use monoliths), event sourcing details (use event-driven), domain modeling (use domain-driven-design)
license: MIT
metadata:
displayName: "Microservices"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Martin Fowler — Microservices Guide"
url: "https://martinfowler.com/microservices/"
- title: "Microservices — Wikipedia"
url: "https://en.wikipedia.org/wiki/Microservices"
---
# Microservices Architecture
## Overview
Microservices architecture structures an application as a collection of loosely coupled, independently deployable services, each organized around a business capability. Each service owns its data, runs in its own process, and communicates over the network.
The canonical reference is Sam Newman's *Building Microservices* (O'Reilly, 2nd edition 2021), supplemented by *Monolith to Microservices* (Newman, 2019) for migration strategies.
**The First Rule of Microservices:** Don't start with microservices. Start with a monolith, understand your domain, and decompose when you have evidence that the benefits outweigh the operational costs. (See `dev/architecture/monoliths` for the monolith-first approach.)
## Service Decomposition
### By Business Capability
Align services to what the business does (e.g., Order Management, Inventory, Payments). This creates stable boundaries because business capabilities change less frequently than technical layers.
### By Subdomain (DDD-Aligned)
Use Domain-Driven Design bounded contexts as service boundaries (see `dev/architecture/domain-driven-design`):
- **Core subdomains** -- Your competitive advantage; build custom services.
- **Supporting subdomains** -- Necessary but not differentiating; simpler services or libraries.
- **Generic subdomains** -- Commodity; buy or use off-the-shelf (auth, email, payments).
### Decomposition Heuristics
| Heuristic | Description |
|-----------|-------------|
| **Single Responsibility** | Each service does one thing well |
| **Data ownership** | Each service owns its data; no shared databases |
| **Independent deployability** | Changing one service does not require deploying another |
| **Team alignment** | One team can own and operate the service end-to-end |
| **Bounded context boundary** | Service boundaries align with DDD bounded contexts |
## Inter-Service Communication
### Synchronous Communication
| Pattern | Protocol | When to Use |
|---------|----------|-------------|
| **Request/Response (REST)** | HTTP/JSON | Simple CRUD, external APIs, broad tooling support |
| **Request/Response (gRPC)** | HTTP/2 + Protobuf | Internal service-to-service; high throughput, strong typing, streaming |
| **GraphQL** | HTTP/JSON | Client-driven queries; aggregating multiple services for a frontend |
### Asynchronous Communication
| Pattern | Mechanism | When to Use |
|---------|-----------|-------------|
| **Event Notification** | Message broker (topic/pub-sub) | Decoupled notification; consumers decide what to do |
| **Event-Carried State Transfer** | Message broker with payload | Reduce synchronous callbacks; consumer has needed data |
| **Command Message** | Message broker (queue) | Tell a specific service to do something |
| **Async Request/Response** | Correlation ID + reply queue | Need a response but don't want to block |
**Rule of thumb:** Prefer asynchronous communication for inter-service calls. Use synchronous only when a real-time response is required (e.g., user-facing request/response).
### Communication Anti-Patterns
- **Distributed monolith** -- Services are "microservices" in name only; they deploy together, share databases, or cannot function independently.
- **Chatty interfaces** -- Excessive synchronous calls between services creating latency chains.
- **Shared database** -- Multiple services reading/writing the same tables destroys independent deployability.
## API Gateway
An API gateway sits between external clients and internal services, providing:
- **Request routing** -- Routes client requests to the appropriate microservice
- **Protocol translation** -- External REST to internal gRPC, for example
- **Authentication/Authorization** -- Centralized security enforcement
- **Rate limiting and throttling** -- Protect services from traffic spikes
- **Response aggregation** -- Combine responses from multiple services for a single client call
Common implementations: Kong, AWS API Gateway, Azure API Management, Envoy, NGINX, Ocelot (.NET).
## Service Mesh
A service mesh handles service-to-service networking concerns transparently via sidecar proxies:
```
┌──────────────────────┐ ┌──────────────────────┐
│ Service A │ │ Service B │
│ ┌────────────────┐ │ │ ┌────────────────┐ │
│ │ App Container │ │ │ │ App Container │ │
│ └───────┬────────┘ │ │ └───────▲────────┘ │
│ │ │ │ │ │
│ ┌───────▼────────┐ │ │ ┌───────┴────────┐ │
│ │ Sidecar Proxy │──┼────┼─▶│ Sidecar Proxy │ │
│ │ (Envoy) │ │ │ │ (Envoy) │ │
│ └────────────────┘ │ │ └────────────────┘ │
└──────────────────────┘ └──────────────────────┘
Control Plane (Istio / Linkerd)
```
**Capabilities:** Mutual TLS, traffic management, retries, circuit breaking, observability (distributed tracing, metrics), canary deployments.
**Implementations:** Istio, Linkerd, Consul Connect, AWS App Mesh.
## Saga Pattern -- Distributed Transactions
Since each microservice owns its data, distributed transactions (2PC) are impractical. The saga pattern manages data consistency across services through a sequence of local transactions with compensating actions.
### Choreography (Event-Driven)
Each service publishes events that trigger the next step. No central coordinator.
```
Order Service ──(OrderCreated)──▶ Payment Service
Payment Service ──(PaymentProcessed)──▶ Inventory Service
Inventory Service ──(InventoryReserved)──▶ Shipping Service
On failure:
Inventory Service ──(ReservationFailed)──▶ Payment Service (refund)
Payment Service ──(RefundProcessed)──▶ Order Service (cancel)
```
**Pros:** Simple, decoupled, no single point of failure.
**Cons:** Hard to understand the overall flow; debugging is difficult; risk of cyclic dependencies.
### Orchestration (Central Coordinator)
A saga orchestrator (process manager) coordinates the steps explicitly.
```
┌─────────────────┐
│ Saga Orchestrator│
│ (Order Saga) │
└────┬───┬───┬────┘
│ │ │
▼ ▼ ▼
Payment Inventory Shipping
Service Service Service
```
**Pros:** Clear flow, easier to understand and debug, centralized compensation logic.
**Cons:** Orchestrator is a coupling point; risk of becoming a "god service."
**Guidance:** Use choreography for simple sagas (2-3 steps). Use orchestration for complex flows (4+ steps or complex compensation).
## Distributed Data Management
| Pattern | Description |
|---------|-------------|
| **Database per Service** | Each service has its own database; no shared access |
| **API Composition** | Query multiple services and aggregate results |
| **CQRS** | Separate read and write models for different optimization (see `dev/architecture/event-driven`) |
| **Event Sourcing** | Store state changes as events; derive current state (see `dev/architecture/event-driven`) |
| **Saga** | Manage distributed transactions through compensating actions |
| **Outbox Pattern** | Reliably publish events by writing to a local outbox table within the same transaction |
## Service Discovery
Services need to find each other in a dynamic environment where instances come and go.
| Approach | Examples | Mechanism |
|----------|----------|-----------|
| **Client-side discovery** | Netflix Eureka, Consul | Client queries registry, picks instance |
| **Server-side discovery** | AWS ALB, Kubernetes Services | Load balancer/proxy routes to available instance |
| **DNS-based** | Consul DNS, Kubernetes CoreDNS | Resolve service name to IP(s) via DNS |
In Kubernetes environments, server-side discovery via Services and DNS is the default and usually sufficient.
## Resilience Patterns
| Pattern | Purpose |
|---------|---------|
| **Circuit Breaker** | Stop calling a failing service; fail fast and allow recovery |
| **Retry with Backoff** | Retry transient failures with exponential backoff and jitter |
| **Bulkhead** | Isolate failures to prevent cascading (separate thread pools / connections) |
| **Timeout** | Set explicit timeouts on all remote calls; never wait forever |
| **Fallback** | Provide degraded but functional response when a service is unavailable |
| **Health Check** | Expose liveness and readiness endpoints for orchestrators |
## When NOT to Use Microservices
Microservices introduce significant operational complexity. Do not use them when:
- **Your team is small** (< 8-10 developers) -- The overhead exceeds the benefit.
- **Your domain is not well understood** -- You will draw the wrong boundaries and create a distributed monolith.
- **You lack operational maturity** -- You need CI/CD, monitoring, distributed tracing, container orchestration, and on-call practices before microservices are viable.
- **Latency is critical** -- Every network hop adds latency; monoliths have zero network overhead for internal calls.
- **Strong consistency is required everywhere** -- Microservices embrace eventual consistency; if your domain requires ACID transactions across multiple entities, a monolith may be simpler.
- **You are building an MVP or prototype** -- Speed of iteration matters more than scalability at this stage.
## Tradeoffs Summary
| Benefit | Cost |
|---------|------|
| Independent deployability | Operational complexity (CI/CD per service, monitoring, tracing) |
| Technology heterogeneity | Polyglot overhead; harder to maintain standards |
| Team autonomy | Coordination overhead; contract management |
| Scalability per service | Network latency; serialization/deserialization cost |
| Fault isolation | Distributed failure modes (partial failures, network partitions) |
| Organizational alignment | Requires mature DevOps culture |
## Best Practices
- Design for failure from day one: circuit breakers, retries, timeouts, bulkheads.
- Own your data: one database per service, no shared database access.
- Make inter-service communication observable: distributed tracing (OpenTelemetry), centralized logging, metrics.
- Use consumer-driven contract testing (Pact, Spring Cloud Contract) to prevent breaking changes.
- Prefer asynchronous communication; use synchronous calls only when necessary.
- Keep services small enough to be owned by a single team, but large enough to justify the operational overhead.
- Deploy independently, test independently, fail independently.
architecture/monoliths/AGENTS.md
# Monolithic Architecture
## Overview
A monolith is a single deployable unit containing all application functionality. Despite the industry's enthusiasm for microservices, monoliths remain the right choice for many -- perhaps most -- systems. The key distinction is between a well-structured monolith (modular, maintainable, intentional) and a poorly structured one (Big Ball of Mud).
This skill covers when and how to build a good monolith, how to structure it for maintainability, and how to migrate away from it incrementally when the time comes.
## Canonical Works
| Book | Author(s) | Focus |
|------|-----------|-------|
| *Monolith to Microservices* | Sam Newman | Migration strategies, Strangler Fig, decomposition patterns |
| *Building Microservices* (Ch. 2) | Sam Newman | Monolith-first approach rationale |
| *Fundamentals of Software Architecture* | Richards & Ford | Layered and modular monolith styles |
## Monolith-First Strategy (Martin Fowler)
Martin Fowler's influential guidance: **"Almost all the successful microservice stories have started with a monolith that got too big and was broken up."**
The rationale:
1. **You don't know your domain boundaries yet.** Getting service boundaries wrong in a microservices architecture is very expensive -- you get a distributed monolith. In a monolith, moving code between modules is a refactor, not a distributed systems problem.
2. **Microservices have high operational overhead.** You need CI/CD per service, distributed tracing, service mesh, contract testing. A small team cannot afford this overhead on day one.
3. **Monoliths are faster to develop initially.** In-process calls are simpler, faster, and more reliable than network calls.
**Strategy:** Start with a well-structured modular monolith. Understand your domain. When a specific module needs independent scalability, deployability, or team ownership, extract it as a service.
## Types of Monoliths
### The Big Ball of Mud (Anti-Pattern)
No discernible structure. Any component depends on any other. Changes in one area cause unexpected failures elsewhere. The codebase resists change.
**Symptoms:**
- No clear module boundaries
- Circular dependencies everywhere
- "Touching one thing breaks something else"
- No one understands the full system
- Fear of refactoring
- Extremely long build and test times
### Layered Monolith
Traditional N-tier architecture: Presentation -> Business Logic -> Data Access. Simple and well-understood, but layers are a poor decomposition axis -- a single feature change often cuts across all layers.
```
┌──────────────────────────┐
│ Presentation Layer │
├──────────────────────────┤
│ Business Logic Layer │
├──────────────────────────┤
│ Data Access Layer │
├──────────────────────────┤
│ Database │
└──────────────────────────┘
```
**Limitations:** Layers encourage technical decomposition instead of domain decomposition. A change to "Order processing" touches all three layers.
### Modular Monolith (Recommended)
A single deployable unit organized into **domain-aligned modules** with well-defined boundaries, explicit internal APIs, and minimal cross-module dependencies. Each module encapsulates its own data, business logic, and (optionally) its own database schema or tables.
```
┌─────────────────────────────────────────────────┐
│ Monolith Process │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Orders │ │ Inventory │ │ Payments │ │
│ │ │ │ │ │ │ │
│ │ - Domain │ │ - Domain │ │ - Domain │ │
│ │ - Data │ │ - Data │ │ - Data │ │
│ │ - API │ │ - API │ │ - API │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ Internal Module APIs │
│ (interfaces, not direct access) │
└─────────────────────────────────────────────────┘
```
## Modular Monolith Design Principles
### 1. Modules as Packages/Assemblies
Each module is a separate package, assembly, or project within the solution. This enables compile-time enforcement of boundaries.
```
src/
Ordering/
Ordering.Domain/
Ordering.Application/
Ordering.Infrastructure/
Ordering.Api/ # Internal API (interface)
Inventory/
Inventory.Domain/
Inventory.Application/
Inventory.Infrastructure/
Inventory.Api/
Payments/
Payments.Domain/
Payments.Application/
Payments.Infrastructure/
Payments.Api/
Host/ # Composition root; wires modules together
```
### 2. Internal APIs (Module Contracts)
Modules communicate through **explicitly defined interfaces**, not by reaching into each other's internals. A module exposes a public API (interface + DTOs) and hides everything else.
```
// Inventory module's public API
public interface IInventoryModule
{
Task<bool> CheckAvailability(string sku, int quantity);
Task ReserveStock(string sku, int quantity, Guid orderId);
Task ReleaseReservation(Guid orderId);
}
```
Other modules depend only on this interface. The implementation is internal to the Inventory module.
### 3. Shared Nothing Data
Each module owns its data. Options for enforcement:
- **Separate schemas** -- Each module gets its own database schema (e.g., `ordering.orders`, `inventory.stock`).
- **Separate tables with no cross-module foreign keys** -- Modules reference each other by ID, not by FK.
- **Separate databases** -- Strongest isolation; easiest microservice extraction path.
**Critical rule:** No module reads or writes another module's tables directly. All data access goes through the module's public API.
### 4. Module Communication Patterns
| Pattern | Description | When to Use |
|---------|-------------|-------------|
| **Direct method call** | Module A calls Module B's interface | Simple, synchronous operations |
| **In-process events** | Module A publishes an event; Module B subscribes | Decoupled reactions; eventual consistency acceptable |
| **Shared mediator** | Use MediatR or similar for commands/queries/notifications | CQRS-style within the monolith |
### 5. Enforce Boundaries
Use architecture testing tools to prevent boundary violations:
- **ArchUnit** (Java) / **NetArchTest** (.NET) -- Write tests that assert module dependency rules.
- **Dependency analysis** -- Fail the build if a module depends on another module's internals.
- **Access modifiers** -- Use `internal` (C#), package-private (Java), or module visibility to hide implementation.
## The Strangler Fig Pattern
When a monolith needs to be incrementally migrated to microservices, the Strangler Fig pattern (named by Martin Fowler after the strangler fig tree) allows you to **gradually replace** monolith functionality without a risky big-bang rewrite.
```
Phase 1: Route all traffic through a facade
┌──────────┐ ┌──────────┐ ┌──────────────────┐
│ Client │───▶│ Facade │───▶│ Monolith │
└──────────┘ └──────────┘ │ (all features) │
└──────────────────┘
Phase 2: Extract one feature into a new service
┌──────────┐ ┌──────────┐ ┌──────────────────┐
│ Client │───▶│ Facade │─┬─▶│ Monolith │
└──────────┘ └──────────┘ │ │ (minus Orders) │
│ └──────────────────┘
│ ┌──────────────────┐
└─▶│ Order Service │
└──────────────────┘
Phase 3: Continue extracting until the monolith shrinks or disappears
```
### Strangler Fig Steps
1. **Identify** a module or feature to extract (start with the one that benefits most from independence).
2. **Implement** the new service alongside the monolith.
3. **Redirect** traffic for that feature from monolith to new service (via routing layer, API gateway, or feature flag).
4. **Remove** the old code from the monolith once the new service is proven.
5. **Repeat** for the next feature.
### Migration Anti-Patterns
- **Big Bang rewrite** -- Attempting to rewrite the entire monolith at once. Almost always fails.
- **Extracting services before understanding the domain** -- You will draw wrong boundaries; fix the monolith's module structure first.
- **Shared database during migration** -- Creates invisible coupling between monolith and service. Use data replication or APIs instead.
## When a Monolith Is the RIGHT Choice
A monolith is likely the right architecture when:
- **Small team (< 8-10 developers)** -- Microservice overhead exceeds the benefit.
- **New product / startup / MVP** -- Speed of iteration matters more than scale. You need to learn the domain first.
- **Simple or well-understood domain** -- Not enough complexity to justify distributed systems.
- **Strong consistency requirements** -- ACID transactions within a single database are much simpler than distributed sagas.
- **Limited operational maturity** -- If you don't have CI/CD, monitoring, distributed tracing, and container orchestration, microservices will hurt more than help.
- **Performance-sensitive workloads** -- In-process calls (nanoseconds) vs. network calls (milliseconds). No serialization/deserialization overhead.
**Remember:** A well-structured modular monolith is not a compromise -- it is a deliberate, valid architecture choice.
## Monolith vs. Microservices Tradeoff Summary
| Dimension | Monolith | Microservices |
|-----------|----------|---------------|
| Deployment | Single unit; all-or-nothing | Independent per service |
| Data consistency | Strong (ACID) | Eventual (sagas, compensation) |
| Operational cost | Low (one thing to run) | High (many things to run) |
| Team coupling | Teams share codebase | Teams own services end-to-end |
| Technology flexibility | Single tech stack | Polyglot possible |
| Refactoring cost | Low (IDE refactoring) | High (contract changes, API versioning) |
| Network overhead | None (in-process) | Significant (serialization, latency) |
| Understanding the system | Easier (one codebase) | Harder (distributed tracing needed) |
## Best Practices
- If you choose a monolith, invest in modular structure from day one. A Big Ball of Mud is a choice, not an inevitability.
- Enforce module boundaries with architecture tests (ArchUnit, NetArchTest).
- Keep modules loosely coupled: depend on interfaces, not implementations.
- Make each module independently testable.
- Monitor module complexity (cyclomatic complexity, coupling metrics) as early warnings for when extraction may be needed.
- When migrating, use the Strangler Fig pattern. Never attempt a big-bang rewrite.
- A monolith that is well-structured and maintainable is better than microservices that are poorly understood and operationally fragile.
architecture/monoliths/metadata.json
{
"version": "1.0.0",
"name": "monoliths",
"displayName": "Monoliths",
"description": "Monolithic architecture patterns including modular monolith design, monolith-first strategy, and migration paths to microservices via the Strangler Fig pattern.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "Martin Fowler — Monolith First",
"url": "https://martinfowler.com/bliki/MonolithFirst.html"
},
{
"title": "Monolithic Application — Wikipedia",
"url": "https://en.wikipedia.org/wiki/Monolithic_application"
}
]
}
architecture/monoliths/README.md
# Monoliths
Monolithic architecture patterns including modular monolith design, monolith-first strategy, and migration paths to microservices via the Strangler Fig pattern.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 7 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/architecture/monoliths
```
## License
MIT
architecture/monoliths/rules/_sections.md
# Monoliths Rules
Best practices and rules for Monoliths.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | If you choose a monolith, invest in modular structure from... | MEDIUM | [`monoliths-if-you-choose-a-monolith-invest-in-modular-structure-from.md`](monoliths-if-you-choose-a-monolith-invest-in-modular-structure-from.md) |
| 2 | Enforce module boundaries with architecture tests... | HIGH | [`monoliths-enforce-module-boundaries-with-architecture-tests.md`](monoliths-enforce-module-boundaries-with-architecture-tests.md) |
| 3 | Keep modules loosely coupled | MEDIUM | [`monoliths-keep-modules-loosely-coupled.md`](monoliths-keep-modules-loosely-coupled.md) |
| 4 | Make each module independently testable | MEDIUM | [`monoliths-make-each-module-independently-testable.md`](monoliths-make-each-module-independently-testable.md) |
| 5 | Monitor module complexity (cyclomatic complexity, coupling... | MEDIUM | [`monoliths-monitor-module-complexity-cyclomatic-complexity-coupling.md`](monoliths-monitor-module-complexity-cyclomatic-complexity-coupling.md) |
| 6 | When migrating, use the Strangler Fig pattern | CRITICAL | [`monoliths-when-migrating-use-the-strangler-fig-pattern.md`](monoliths-when-migrating-use-the-strangler-fig-pattern.md) |
| 7 | A monolith that is well-structured and maintainable is... | MEDIUM | [`monoliths-a-monolith-that-is-well-structured-and-maintainable-is.md`](monoliths-a-monolith-that-is-well-structured-and-maintainable-is.md) |
architecture/monoliths/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: monoliths, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
architecture/monoliths/rules/monoliths-a-monolith-that-is-well-structured-and-maintainable-is.md
---
title: "A monolith that is well-structured and maintainable is..."
impact: MEDIUM
impactDescription: "general best practice"
tags: monoliths, dev, architecture, monolith-first-strategy, modular-monolith-design, monolith-decomposition
---
## A monolith that is well-structured and maintainable is...
A monolith that is well-structured and maintainable is better than microservices that are poorly understood and operationally fragile.
architecture/monoliths/rules/monoliths-enforce-module-boundaries-with-architecture-tests.md
---
title: "Enforce module boundaries with architecture tests..."
impact: HIGH
impactDescription: "significant quality or reliability improvement"
tags: monoliths, dev, architecture, monolith-first-strategy, modular-monolith-design, monolith-decomposition
---
## Enforce module boundaries with architecture tests...
Enforce module boundaries with architecture tests (ArchUnit, NetArchTest).
architecture/monoliths/rules/monoliths-if-you-choose-a-monolith-invest-in-modular-structure-from.md
---
title: "If you choose a monolith, invest in modular structure from..."
impact: MEDIUM
impactDescription: "general best practice"
tags: monoliths, dev, architecture, monolith-first-strategy, modular-monolith-design, monolith-decomposition
---
## If you choose a monolith, invest in modular structure from...
If you choose a monolith, invest in modular structure from day one. A Big Ball of Mud is a choice, not an inevitability.
architecture/monoliths/rules/monoliths-keep-modules-loosely-coupled.md
---
title: "Keep modules loosely coupled"
impact: MEDIUM
impactDescription: "general best practice"
tags: monoliths, dev, architecture, monolith-first-strategy, modular-monolith-design, monolith-decomposition
---
## Keep modules loosely coupled
Keep modules loosely coupled: depend on interfaces, not implementations.
architecture/monoliths/rules/monoliths-make-each-module-independently-testable.md
---
title: "Make each module independently testable"
impact: MEDIUM
impactDescription: "general best practice"
tags: monoliths, dev, architecture, monolith-first-strategy, modular-monolith-design, monolith-decomposition
---
## Make each module independently testable
Make each module independently testable.
architecture/monoliths/rules/monoliths-monitor-module-complexity-cyclomatic-complexity-coupling.md
---
title: "Monitor module complexity (cyclomatic complexity, coupling..."
impact: MEDIUM
impactDescription: "general best practice"
tags: monoliths, dev, architecture, monolith-first-strategy, modular-monolith-design, monolith-decomposition
---
## Monitor module complexity (cyclomatic complexity, coupling...
Monitor module complexity (cyclomatic complexity, coupling metrics) as early warnings for when extraction may be needed.
architecture/monoliths/rules/monoliths-when-migrating-use-the-strangler-fig-pattern.md
---
title: "When migrating, use the Strangler Fig pattern"
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: monoliths, dev, architecture, monolith-first-strategy, modular-monolith-design, monolith-decomposition
---
## When migrating, use the Strangler Fig pattern
When migrating, use the Strangler Fig pattern. Never attempt a big-bang rewrite.
architecture/monoliths/SKILL.md
---
name: monoliths
description: |
Monolithic architecture patterns including modular monolith design, monolith-first strategy, and migration paths to microservices via the Strangler Fig pattern.
USE FOR: monolith-first strategy, modular monolith design, monolith decomposition, Strangler Fig migration, avoiding Big Ball of Mud, when to keep a monolith
DO NOT USE FOR: microservice decomposition (use microservices), event-driven architecture (use event-driven), clean architecture layers (use dev/craftsmanship/clean-architecture)
license: MIT
metadata:
displayName: "Monoliths"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Martin Fowler — Monolith First"
url: "https://martinfowler.com/bliki/MonolithFirst.html"
- title: "Monolithic Application — Wikipedia"
url: "https://en.wikipedia.org/wiki/Monolithic_application"
---
# Monolithic Architecture
## Overview
A monolith is a single deployable unit containing all application functionality. Despite the industry's enthusiasm for microservices, monoliths remain the right choice for many -- perhaps most -- systems. The key distinction is between a well-structured monolith (modular, maintainable, intentional) and a poorly structured one (Big Ball of Mud).
This skill covers when and how to build a good monolith, how to structure it for maintainability, and how to migrate away from it incrementally when the time comes.
## Canonical Works
| Book | Author(s) | Focus |
|------|-----------|-------|
| *Monolith to Microservices* | Sam Newman | Migration strategies, Strangler Fig, decomposition patterns |
| *Building Microservices* (Ch. 2) | Sam Newman | Monolith-first approach rationale |
| *Fundamentals of Software Architecture* | Richards & Ford | Layered and modular monolith styles |
## Monolith-First Strategy (Martin Fowler)
Martin Fowler's influential guidance: **"Almost all the successful microservice stories have started with a monolith that got too big and was broken up."**
The rationale:
1. **You don't know your domain boundaries yet.** Getting service boundaries wrong in a microservices architecture is very expensive -- you get a distributed monolith. In a monolith, moving code between modules is a refactor, not a distributed systems problem.
2. **Microservices have high operational overhead.** You need CI/CD per service, distributed tracing, service mesh, contract testing. A small team cannot afford this overhead on day one.
3. **Monoliths are faster to develop initially.** In-process calls are simpler, faster, and more reliable than network calls.
**Strategy:** Start with a well-structured modular monolith. Understand your domain. When a specific module needs independent scalability, deployability, or team ownership, extract it as a service.
## Types of Monoliths
### The Big Ball of Mud (Anti-Pattern)
No discernible structure. Any component depends on any other. Changes in one area cause unexpected failures elsewhere. The codebase resists change.
**Symptoms:**
- No clear module boundaries
- Circular dependencies everywhere
- "Touching one thing breaks something else"
- No one understands the full system
- Fear of refactoring
- Extremely long build and test times
### Layered Monolith
Traditional N-tier architecture: Presentation -> Business Logic -> Data Access. Simple and well-understood, but layers are a poor decomposition axis -- a single feature change often cuts across all layers.
```
┌──────────────────────────┐
│ Presentation Layer │
├──────────────────────────┤
│ Business Logic Layer │
├──────────────────────────┤
│ Data Access Layer │
├──────────────────────────┤
│ Database │
└──────────────────────────┘
```
**Limitations:** Layers encourage technical decomposition instead of domain decomposition. A change to "Order processing" touches all three layers.
### Modular Monolith (Recommended)
A single deployable unit organized into **domain-aligned modules** with well-defined boundaries, explicit internal APIs, and minimal cross-module dependencies. Each module encapsulates its own data, business logic, and (optionally) its own database schema or tables.
```
┌─────────────────────────────────────────────────┐
│ Monolith Process │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Orders │ │ Inventory │ │ Payments │ │
│ │ │ │ │ │ │ │
│ │ - Domain │ │ - Domain │ │ - Domain │ │
│ │ - Data │ │ - Data │ │ - Data │ │
│ │ - API │ │ - API │ │ - API │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ Internal Module APIs │
│ (interfaces, not direct access) │
└─────────────────────────────────────────────────┘
```
## Modular Monolith Design Principles
### 1. Modules as Packages/Assemblies
Each module is a separate package, assembly, or project within the solution. This enables compile-time enforcement of boundaries.
```
src/
Ordering/
Ordering.Domain/
Ordering.Application/
Ordering.Infrastructure/
Ordering.Api/ # Internal API (interface)
Inventory/
Inventory.Domain/
Inventory.Application/
Inventory.Infrastructure/
Inventory.Api/
Payments/
Payments.Domain/
Payments.Application/
Payments.Infrastructure/
Payments.Api/
Host/ # Composition root; wires modules together
```
### 2. Internal APIs (Module Contracts)
Modules communicate through **explicitly defined interfaces**, not by reaching into each other's internals. A module exposes a public API (interface + DTOs) and hides everything else.
```
// Inventory module's public API
public interface IInventoryModule
{
Task<bool> CheckAvailability(string sku, int quantity);
Task ReserveStock(string sku, int quantity, Guid orderId);
Task ReleaseReservation(Guid orderId);
}
```
Other modules depend only on this interface. The implementation is internal to the Inventory module.
### 3. Shared Nothing Data
Each module owns its data. Options for enforcement:
- **Separate schemas** -- Each module gets its own database schema (e.g., `ordering.orders`, `inventory.stock`).
- **Separate tables with no cross-module foreign keys** -- Modules reference each other by ID, not by FK.
- **Separate databases** -- Strongest isolation; easiest microservice extraction path.
**Critical rule:** No module reads or writes another module's tables directly. All data access goes through the module's public API.
### 4. Module Communication Patterns
| Pattern | Description | When to Use |
|---------|-------------|-------------|
| **Direct method call** | Module A calls Module B's interface | Simple, synchronous operations |
| **In-process events** | Module A publishes an event; Module B subscribes | Decoupled reactions; eventual consistency acceptable |
| **Shared mediator** | Use MediatR or similar for commands/queries/notifications | CQRS-style within the monolith |
### 5. Enforce Boundaries
Use architecture testing tools to prevent boundary violations:
- **ArchUnit** (Java) / **NetArchTest** (.NET) -- Write tests that assert module dependency rules.
- **Dependency analysis** -- Fail the build if a module depends on another module's internals.
- **Access modifiers** -- Use `internal` (C#), package-private (Java), or module visibility to hide implementation.
## The Strangler Fig Pattern
When a monolith needs to be incrementally migrated to microservices, the Strangler Fig pattern (named by Martin Fowler after the strangler fig tree) allows you to **gradually replace** monolith functionality without a risky big-bang rewrite.
```
Phase 1: Route all traffic through a facade
┌──────────┐ ┌──────────┐ ┌──────────────────┐
│ Client │───▶│ Facade │───▶│ Monolith │
└──────────┘ └──────────┘ │ (all features) │
└──────────────────┘
Phase 2: Extract one feature into a new service
┌──────────┐ ┌──────────┐ ┌──────────────────┐
│ Client │───▶│ Facade │─┬─▶│ Monolith │
└──────────┘ └──────────┘ │ │ (minus Orders) │
│ └──────────────────┘
│ ┌──────────────────┐
└─▶│ Order Service │
└──────────────────┘
Phase 3: Continue extracting until the monolith shrinks or disappears
```
### Strangler Fig Steps
1. **Identify** a module or feature to extract (start with the one that benefits most from independence).
2. **Implement** the new service alongside the monolith.
3. **Redirect** traffic for that feature from monolith to new service (via routing layer, API gateway, or feature flag).
4. **Remove** the old code from the monolith once the new service is proven.
5. **Repeat** for the next feature.
### Migration Anti-Patterns
- **Big Bang rewrite** -- Attempting to rewrite the entire monolith at once. Almost always fails.
- **Extracting services before understanding the domain** -- You will draw wrong boundaries; fix the monolith's module structure first.
- **Shared database during migration** -- Creates invisible coupling between monolith and service. Use data replication or APIs instead.
## When a Monolith Is the RIGHT Choice
A monolith is likely the right architecture when:
- **Small team (< 8-10 developers)** -- Microservice overhead exceeds the benefit.
- **New product / startup / MVP** -- Speed of iteration matters more than scale. You need to learn the domain first.
- **Simple or well-understood domain** -- Not enough complexity to justify distributed systems.
- **Strong consistency requirements** -- ACID transactions within a single database are much simpler than distributed sagas.
- **Limited operational maturity** -- If you don't have CI/CD, monitoring, distributed tracing, and container orchestration, microservices will hurt more than help.
- **Performance-sensitive workloads** -- In-process calls (nanoseconds) vs. network calls (milliseconds). No serialization/deserialization overhead.
**Remember:** A well-structured modular monolith is not a compromise -- it is a deliberate, valid architecture choice.
## Monolith vs. Microservices Tradeoff Summary
| Dimension | Monolith | Microservices |
|-----------|----------|---------------|
| Deployment | Single unit; all-or-nothing | Independent per service |
| Data consistency | Strong (ACID) | Eventual (sagas, compensation) |
| Operational cost | Low (one thing to run) | High (many things to run) |
| Team coupling | Teams share codebase | Teams own services end-to-end |
| Technology flexibility | Single tech stack | Polyglot possible |
| Refactoring cost | Low (IDE refactoring) | High (contract changes, API versioning) |
| Network overhead | None (in-process) | Significant (serialization, latency) |
| Understanding the system | Easier (one codebase) | Harder (distributed tracing needed) |
## Best Practices
- If you choose a monolith, invest in modular structure from day one. A Big Ball of Mud is a choice, not an inevitability.
- Enforce module boundaries with architecture tests (ArchUnit, NetArchTest).
- Keep modules loosely coupled: depend on interfaces, not implementations.
- Make each module independently testable.
- Monitor module complexity (cyclomatic complexity, coupling metrics) as early warnings for when extraction may be needed.
- When migrating, use the Strangler Fig pattern. Never attempt a big-bang rewrite.
- A monolith that is well-structured and maintainable is better than microservices that are poorly understood and operationally fragile.
architecture/README.md
# Architecture
Use when selecting architecture styles, evaluating system decomposition strategies, or analyzing architecture characteristics (quality attributes) for a system.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 6 individual best practice rules |
## Sub-skills
| Skill | Description |
|-------|-------------|
| [`domain-driven-design/`](domain-driven-design/) | Domain-Driven Design (DDD) strategic and tactical patterns based on Eric Evans' "Domain-Driven Design" -- covering bound... |
| [`event-driven/`](event-driven/) | Event-Driven Architecture (EDA), Event Sourcing, and CQRS -- complementary but independent patterns for building reactiv... |
| [`hexagonal/`](hexagonal/) | Hexagonal Architecture (Ports and Adapters), Onion Architecture, and their relationship to Clean Architecture -- enablin... |
| [`microservices/`](microservices/) | Microservice architecture patterns and practices based on Sam Newman's "Building Microservices" -- covering service deco... |
| [`monoliths/`](monoliths/) | Monolithic architecture patterns including modular monolith design, monolith-first strategy, and migration paths to micr... |
| [`well-architected/`](well-architected/) | Cloud well-architected frameworks from AWS, Azure, and GCP -- covering pillars, design principles, review processes, and... |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/architecture
```
## License
MIT
architecture/rules/_sections.md
# Architecture Rules
Best practices and rules for Architecture.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Start with the simplest architecture that meets your... | MEDIUM | [`architecture-start-with-the-simplest-architecture-that-meets-your.md`](architecture-start-with-the-simplest-architecture-that-meets-your.md) |
| 2 | Make architecture decisions explicit and documented (ADRs) | MEDIUM | [`architecture-make-architecture-decisions-explicit-and-documented-adrs.md`](architecture-make-architecture-decisions-explicit-and-documented-adrs.md) |
| 3 | Architecture is not a one-time activity -- it is continuous | MEDIUM | [`architecture-is-not-a-one-time-activity-it-is-continuous.md`](architecture-is-not-a-one-time-activity-it-is-continuous.md) |
| 4 | Align architecture boundaries with team boundaries (see... | MEDIUM | [`architecture-align-architecture-boundaries-with-team-boundaries-see.md`](architecture-align-architecture-boundaries-with-team-boundaries-see.md) |
| 5 | Use fitness functions to objectively measure whether the... | MEDIUM | [`architecture-use-fitness-functions-to-objectively-measure-whether-the.md`](architecture-use-fitness-functions-to-objectively-measure-whether-the.md) |
| 6 | Understand that the "best" architecture depends on your... | HIGH | [`architecture-understand-that-the-best-architecture-depends-on-your.md`](architecture-understand-that-the-best-architecture-depends-on-your.md) |
architecture/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: architecture, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
architecture/rules/architecture-align-architecture-boundaries-with-team-boundaries-see.md
---
title: "Align architecture boundaries with team boundaries (see..."
impact: MEDIUM
impactDescription: "general best practice"
tags: architecture, dev, architecture-style-selection, comparing-monolith-vs-microservices, architecture-characteristics-analysis
---
## Align architecture boundaries with team boundaries (see...
Align architecture boundaries with team boundaries (see Conway's Law and the Inverse Conway Maneuver).
architecture/rules/architecture-is-not-a-one-time-activity-it-is-continuous.md
---
title: "Architecture is not a one-time activity -- it is continuous"
impact: MEDIUM
impactDescription: "general best practice"
tags: architecture, dev, architecture-style-selection, comparing-monolith-vs-microservices, architecture-characteristics-analysis
---
## Architecture is not a one-time activity -- it is continuous
Architecture is not a one-time activity -- it is continuous. Revisit decisions as the system and context evolve.
architecture/rules/architecture-make-architecture-decisions-explicit-and-documented-adrs.md
---
title: "Make architecture decisions explicit and documented (ADRs)"
impact: MEDIUM
impactDescription: "general best practice"
tags: architecture, dev, architecture-style-selection, comparing-monolith-vs-microservices, architecture-characteristics-analysis
---
## Make architecture decisions explicit and documented (ADRs)
Make architecture decisions explicit and documented (ADRs).
architecture/rules/architecture-start-with-the-simplest-architecture-that-meets-your.md
---
title: "Start with the simplest architecture that meets your..."
impact: MEDIUM
impactDescription: "general best practice"
tags: architecture, dev, architecture-style-selection, comparing-monolith-vs-microservices, architecture-characteristics-analysis
---
## Start with the simplest architecture that meets your...
Start with the simplest architecture that meets your driving characteristics. Evolve when evidence demands it.
architecture/rules/architecture-understand-that-the-best-architecture-depends-on-your.md
---
title: "Understand that the \"best\" architecture depends on your..."
impact: HIGH
impactDescription: "significant quality or reliability improvement"
tags: architecture, dev, architecture-style-selection, comparing-monolith-vs-microservices, architecture-characteristics-analysis
---
## Understand that the "best" architecture depends on your...
Understand that the "best" architecture depends on your specific context: team size, domain complexity, scale requirements, and organizational structure.
architecture/rules/architecture-use-fitness-functions-to-objectively-measure-whether-the.md
---
title: "Use fitness functions to objectively measure whether the..."
impact: MEDIUM
impactDescription: "general best practice"
tags: architecture, dev, architecture-style-selection, comparing-monolith-vs-microservices, architecture-characteristics-analysis
---
## Use fitness functions to objectively measure whether the...
Use fitness functions to objectively measure whether the architecture meets its goals over time.
architecture/SKILL.md
---
name: architecture
description: |
Use when selecting architecture styles, evaluating system decomposition strategies, or analyzing architecture characteristics (quality attributes) for a system.
USE FOR: architecture style selection, comparing monolith vs microservices, architecture characteristics analysis, system decomposition strategy
DO NOT USE FOR: specific style details (use sub-skills: microservices, monoliths, event-driven, etc.), code-level patterns (use dev/design-patterns), integration messaging (use dev/integration-patterns)
license: MIT
metadata:
displayName: "Architecture"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Martin Fowler — Software Architecture Guide"
url: "https://martinfowler.com/architecture/"
- title: "Software Architecture — Wikipedia"
url: "https://en.wikipedia.org/wiki/Software_architecture"
---
# Software Architecture
## Overview
Software architecture is the set of significant decisions about the organization of a software system -- the selection of structural elements, their interfaces, composition, and the guiding principles that constrain design and evolution over time. Choosing the right architecture style is one of the highest-leverage decisions a team makes; it shapes every subsequent technical and organizational choice.
This skill covers how to reason about architecture styles, evaluate architecture characteristics (quality attributes), and make informed tradeoffs. For deep dives into specific styles, see the sub-skills below.
## Canonical Works
| Book | Author(s) | Focus |
|------|-----------|-------|
| *Fundamentals of Software Architecture* | Mark Richards & Neal Ford | Architecture styles, characteristics, decisions, metrics |
| *Software Architecture: The Hard Parts* | Neal Ford, Mark Richards, Pramod Sadalage, Zhamak Dehghani | Tradeoff analysis, decomposition, data ownership, contracts |
| *Building Evolutionary Architectures* | Ford, Parsons, Kua | Fitness functions, incremental change, governed evolution |
| *Documenting Software Architectures* | Clements et al. | Views, viewpoints, architecture documentation |
## The Monolith-to-Microservices Spectrum
Architecture is not a binary choice between monolith and microservices. It is a spectrum of modularity:
```
Monolith Microservices
| |
| Big Ball Layered Modular Service- Micro- |
| of Mud Monolith Monolith Based services |
| |
◄─────────────────────────────────────────────────────────────►
Less distributed More distributed
Simpler operations Complex operations
Easier consistency Eventual consistency
Tighter coupling Loose coupling
```
**Key insight (Richards & Ford):** Move along the spectrum only when the pain of your current position exceeds the cost of the next step. Start simple; evolve when you have evidence.
## Architecture Characteristics (Quality Attributes)
Architecture characteristics -- also called "-ilities" -- are the non-functional requirements that shape which style fits. They are inherently in tension; optimizing one often degrades another.
| Characteristic | Description | Tension With |
|---------------|-------------|--------------|
| **Scalability** | Ability to handle growing load | Simplicity, Cost |
| **Reliability** | System uptime and fault tolerance | Performance, Cost |
| **Performance** | Latency and throughput | Scalability, Maintainability |
| **Security** | Protection against threats | Usability, Performance |
| **Maintainability** | Ease of change and evolution | Performance, Time-to-Market |
| **Deployability** | Ease and frequency of deployment | Simplicity, Reliability |
| **Testability** | Ease of verifying correctness | Time-to-Market |
| **Elasticity** | Ability to scale up AND down dynamically | Cost, Simplicity |
| **Fault Tolerance** | Graceful degradation under failure | Performance, Complexity |
| **Modularity** | Degree of separation between components | Performance (indirection cost) |
### Identifying Driving Characteristics
Not every characteristic matters equally. Richards & Ford recommend identifying the **top 3-5 driving characteristics** for a system and using those to select an architecture style.
## Architecture Style Comparison
| Style | Scalability | Simplicity | Deployability | Data Consistency | Cost | Best For |
|-------|:-----------:|:----------:|:-------------:|:----------------:|:----:|----------|
| **Layered Monolith** | Low | High | Low | High | Low | Small teams, simple domains |
| **Modular Monolith** | Medium | Medium | Medium | High | Low | Medium complexity, single team |
| **Service-Based** | Medium | Medium | Medium | Medium | Medium | Domain-separated teams |
| **Microservices** | High | Low | High | Low | High | Large orgs, independent teams |
| **Event-Driven** | High | Low | High | Low | Medium | Async workflows, event streams |
| **Space-Based** | Very High | Low | Medium | Low | High | Extreme elastic scalability |
| **Orchestration-Driven** | Medium | Medium | Medium | Medium | Medium | Complex workflows |
| **Pipeline (Pipes & Filters)** | Medium | Medium | Medium | Medium | Low | Data processing, ETL |
## Architecture Decision Records (ADRs)
Every significant architecture decision should be captured in an Architecture Decision Record. ADRs provide context for future developers about why a decision was made, what alternatives were considered, and what tradeoffs were accepted.
See: `specs/documentation/adr` for ADR templates and practices.
**ADR structure (Michael Nygard format):**
- **Title** -- Short noun phrase (e.g., "Use PostgreSQL for order data")
- **Status** -- Proposed, Accepted, Deprecated, Superseded
- **Context** -- The forces at play, the problem, the constraints
- **Decision** -- What was decided and why
- **Consequences** -- What becomes easier, what becomes harder
## Architecture Decision Process
1. **Identify the driving characteristics** -- What are the top 3-5 quality attributes?
2. **Identify the domain partitioning** -- How does the domain decompose? (see `dev/architecture/domain-driven-design`)
3. **Select a candidate style** -- Use the comparison table above to narrow options.
4. **Evaluate tradeoffs** -- Every style has strengths and weaknesses. Make tradeoffs explicit.
5. **Record the decision** -- Write an ADR capturing context, decision, and consequences.
6. **Validate with fitness functions** -- Define measurable criteria that the architecture must satisfy over time.
## Common Anti-Patterns
- **Accidental architecture** -- No deliberate style; the system evolves into a Big Ball of Mud.
- **Resume-driven architecture** -- Choosing microservices (or any style) because it looks good on a resume, not because the problem demands it.
- **Architecture by analogy** -- "Netflix uses microservices, so we should too." Your context is not Netflix's context.
- **Ignoring the First Law of Software Architecture** -- "Everything in software architecture is a tradeoff" (Richards & Ford). If you think you found something that isn't a tradeoff, you haven't identified the tradeoff yet.
## Best Practices
- Start with the simplest architecture that meets your driving characteristics. Evolve when evidence demands it.
- Make architecture decisions explicit and documented (ADRs).
- Architecture is not a one-time activity -- it is continuous. Revisit decisions as the system and context evolve.
- Align architecture boundaries with team boundaries (see Conway's Law and the Inverse Conway Maneuver).
- Use fitness functions to objectively measure whether the architecture meets its goals over time.
- Understand that the "best" architecture depends on your specific context: team size, domain complexity, scale requirements, and organizational structure.
## Sub-Skills
- `dev/architecture/microservices` -- Service decomposition, inter-service communication, saga patterns
- `dev/architecture/monoliths` -- Modular monolith, monolith-first strategy, Strangler Fig migration
- `dev/architecture/well-architected` -- AWS, Azure, and GCP well-architected frameworks
- `dev/architecture/event-driven` -- Event-driven architecture, event sourcing, CQRS
- `dev/architecture/domain-driven-design` -- Bounded contexts, aggregates, strategic and tactical DDD
- `dev/architecture/hexagonal` -- Ports and adapters, onion architecture, dependency inversion
architecture/well-architected/AGENTS.md
# Cloud Well-Architected Frameworks
## Overview
The major cloud providers each publish a Well-Architected Framework -- a set of pillars, design principles, and best practices for building reliable, secure, performant, and cost-effective workloads in the cloud. While the terminology and organization differ, the core concerns are remarkably consistent across all three.
This skill covers all three frameworks in a unified view, enabling cross-cloud comparison and provider-agnostic architecture reasoning.
## Cross-Cloud Pillar Comparison
| Concern | AWS (6 Pillars) | Azure (5 Pillars) | GCP (6 Pillars) |
|---------|-----------------|-------------------|-----------------|
| **Operations** | Operational Excellence | Operational Excellence | Operational Excellence |
| **Security** | Security | Security | Security, Privacy & Compliance |
| **Reliability** | Reliability | Reliability | Reliability |
| **Performance** | Performance Efficiency | Performance Efficiency | Performance Optimization |
| **Cost** | Cost Optimization | Cost Optimization | Cost Optimization |
| **Sustainability** | Sustainability | -- | -- |
| **System Design** | -- | -- | System Design |
**Key observation:** All three frameworks agree on the five core concerns (operations, security, reliability, performance, cost). AWS adds Sustainability; GCP adds System Design as an explicit pillar; Azure covers both implicitly within its five pillars.
---
## AWS Well-Architected Framework (6 Pillars)
### 1. Operational Excellence
Design, run, and monitor systems to deliver business value and continually improve processes and procedures.
**Key principles:**
- Perform operations as code (Infrastructure as Code)
- Make frequent, small, reversible changes
- Refine operations procedures frequently
- Anticipate failure; learn from all operational events
- Use managed services to reduce operational burden
### 2. Security
Protect data, systems, and assets through risk assessments, security controls, and automated security best practices.
**Key principles:**
- Implement a strong identity foundation (least privilege, IAM)
- Enable traceability (logging, auditing, monitoring)
- Apply security at all layers (edge, VPC, subnet, instance, OS, application)
- Automate security best practices
- Protect data in transit and at rest
- Keep people away from data (reduce direct access)
- Prepare for security events (incident response runbooks)
### 3. Reliability
Ensure a workload can recover from failures and meet demand through proper planning and design.
**Key principles:**
- Automatically recover from failure
- Test recovery procedures
- Scale horizontally to increase aggregate availability
- Stop guessing capacity (use auto-scaling)
- Manage change through automation
### 4. Performance Efficiency
Use computing resources efficiently and maintain that efficiency as demand changes and technologies evolve.
**Key principles:**
- Democratize advanced technologies (use managed services)
- Go global in minutes (multi-region)
- Use serverless architectures where possible
- Experiment more often
- Consider mechanical sympathy (understand how services are consumed)
### 5. Cost Optimization
Avoid unnecessary costs and understand where money is being spent.
**Key principles:**
- Implement cloud financial management
- Adopt a consumption model (pay for what you use)
- Measure overall efficiency
- Stop spending money on undifferentiated heavy lifting
- Analyze and attribute expenditure
### 6. Sustainability
Minimize environmental impact of cloud workloads.
**Key principles:**
- Understand your impact
- Establish sustainability goals
- Maximize utilization
- Anticipate and adopt new, more efficient offerings
- Use managed services (shared infrastructure is more efficient)
- Reduce downstream impact of your cloud workloads
---
## Azure Well-Architected Framework (5 Pillars)
### 1. Reliability
Ensure the application meets its availability commitments through resiliency and recovery design.
**Key principles:**
- Design for business requirements (define SLA/SLO/SLI)
- Design for failure (assume everything can fail)
- Observe application health (monitoring, alerting)
- Drive automation (minimize human error)
- Design for self-healing
- Design for scale-out
### 2. Security
Protect the confidentiality, integrity, and availability of the application and its data.
**Key principles:**
- Plan resources and how to harden them
- Automate and use least privilege
- Classify and encrypt data
- Guard with identity management (Zero Trust)
- Monitor security for the entire system
- Secure the supply chain
### 3. Cost Optimization
Balance business goals with budget to create a cost-effective workload while avoiding waste.
**Key principles:**
- Develop cost-management discipline
- Design with a cost-efficiency mindset
- Design for usage optimization (right-size, auto-scale)
- Continuously monitor and optimize
### 4. Operational Excellence
Reduce issues in production by building holistic observability and automated processes.
**Key principles:**
- Embrace DevOps culture
- Establish development standards (IaC, CI/CD)
- Evolve operations with observability
- Deploy with confidence (progressive rollout, rollback)
- Automate for efficiency
- Adopt safe deployment practices
### 5. Performance Efficiency
Efficiently scale your workload to meet demand without over-provisioning or under-provisioning.
**Key principles:**
- Negotiate realistic performance targets (SLAs/SLOs)
- Design to meet capacity requirements
- Achieve and sustain performance
- Improve efficiency through optimization
- Monitor and collect data to measure performance
---
## GCP Architecture Framework (6 Pillars)
### 1. System Design
Design systems that meet functional and non-functional requirements using cloud-native patterns.
**Key principles:**
- Design for change (loosely coupled components)
- Design for automation
- Design for managed services
- Design for portability where appropriate
- Design for observability
### 2. Operational Excellence
Deploy, operate, and monitor systems efficiently with minimal manual intervention.
**Key principles:**
- Automate deployments (CI/CD)
- Practice infrastructure as code
- Monitor and alert on SLIs
- Conduct game days and chaos engineering
- Implement progressive rollouts
### 3. Security, Privacy & Compliance
Protect data and systems, maintain privacy, and meet compliance requirements.
**Key principles:**
- Leverage shared responsibility model
- Apply defense in depth
- Automate security controls
- Classify data by sensitivity
- Implement identity federation and least privilege
- Manage compliance as code
### 4. Reliability
Design and operate a resilient, highly available service that meets availability targets.
**Key principles:**
- Define and measure SLOs/SLIs
- Build redundancy to handle failures
- Design for graceful degradation
- Implement health monitoring and automated remediation
- Test for reliability (disaster recovery, chaos engineering)
### 5. Cost Optimization
Manage and optimize costs while maintaining performance and reliability.
**Key principles:**
- Identify cost drivers
- Right-size and auto-scale resources
- Use committed use discounts and sustained use discounts
- Monitor and forecast costs
- Build a cost-aware culture
### 6. Performance Optimization
Design, validate, and tune resources for optimal performance.
**Key principles:**
- Define performance requirements early
- Benchmark and load-test
- Optimize at the application and infrastructure layers
- Use caching and CDNs
- Monitor performance continuously
---
## Well-Architected Review Process
A Well-Architected Review (WAR) is a structured assessment of a workload against the framework's pillars. All three clouds provide review tooling:
| Cloud | Tool | How It Works |
|-------|------|-------------|
| **AWS** | AWS Well-Architected Tool | Answer questions per pillar; generates findings and improvement plan |
| **Azure** | Azure Well-Architected Review (online assessment) | Self-service questionnaire; generates recommendations |
| **GCP** | Architecture Framework checklists + Cloud Architecture Center | Checklist-driven review; reference architectures |
### Review Steps
1. **Scope the workload** -- Define the boundary of what is being reviewed (a single application, a platform, a service).
2. **Assemble the team** -- Include architects, developers, operations, security, and finance.
3. **Walk through each pillar** -- Answer the framework's questions honestly. Identify gaps.
4. **Prioritize findings** -- Rank by business impact and effort. Focus on high-risk, high-impact items first.
5. **Create an improvement plan** -- Assign owners, set deadlines, track progress.
6. **Schedule regular reviews** -- Architecture is not a one-time activity. Review quarterly or after major changes.
### Review Frequency
| Trigger | Action |
|---------|--------|
| New workload launch | Full review before production |
| Major architecture change | Review affected pillars |
| Quarterly cadence | Lightweight review of all pillars |
| Incident or outage | Review Reliability and Operational Excellence pillars |
| Cost spike | Review Cost Optimization pillar |
## Pillar Tensions and Tradeoffs
The pillars are inherently in tension. Optimizing one often increases costs or complexity in another:
| Tradeoff | Example |
|----------|---------|
| Reliability vs. Cost | Multi-region deployment increases availability but doubles infrastructure cost |
| Security vs. Performance | Encryption at rest and in transit adds latency |
| Performance vs. Cost | Over-provisioning ensures headroom but wastes money |
| Operational Excellence vs. Speed | Comprehensive CI/CD and observability take time to set up but pay off long-term |
| Sustainability vs. Performance | Right-sizing reduces waste but may reduce performance headroom |
**Key principle:** Make tradeoffs explicitly. Document which pillars are prioritized and why (use Architecture Decision Records -- see `specs/documentation/adr`).
## Best Practices
- Use the well-architected framework as a common language between architects, developers, and stakeholders -- not as a compliance checklist.
- Conduct well-architected reviews early and often, not just before launch.
- Prioritize the pillars that matter most for your workload (e.g., a financial system prioritizes Security and Reliability; a data pipeline prioritizes Performance and Cost).
- Leverage the cloud provider's native review tooling to structure the assessment.
- Document all tradeoff decisions in Architecture Decision Records.
- Remember that well-architected is aspirational -- no workload scores perfectly on every pillar. The goal is continuous improvement.
- When working across clouds (multi-cloud or migration), use this cross-cloud comparison to map equivalent concerns and avoid gaps.
architecture/well-architected/metadata.json
{
"version": "1.0.0",
"name": "well-architected",
"displayName": "Well-Architected Frameworks",
"description": "Cloud well-architected frameworks from AWS, Azure, and GCP -- covering pillars, design principles, review processes, and cross-cloud comparison for building reliable, secure, cost-effective cloud workloads.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "AWS Well-Architected Framework",
"url": "https://docs.aws.amazon.com/wellarchitected/latest/framework/welcome.html"
},
{
"title": "Microsoft Azure Well-Architected Framework",
"url": "https://learn.microsoft.com/en-us/azure/well-architected/"
},
{
"title": "Google Cloud Architecture Framework",
"url": "https://cloud.google.com/architecture/framework"
}
]
}
architecture/well-architected/README.md
# Well-Architected Frameworks
Cloud well-architected frameworks from AWS, Azure, and GCP -- covering pillars, design principles, review processes, and cross-cloud comparison for building reliable, secure, cost-effective cloud workloads.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 7 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/architecture/well-architected
```
## License
MIT
architecture/well-architected/rules/_sections.md
# Well-Architected Frameworks Rules
Best practices and rules for Well-Architected Frameworks.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Use the well-architected framework as a common language... | MEDIUM | [`well-architected-use-the-well-architected-framework-as-a-common-language.md`](well-architected-use-the-well-architected-framework-as-a-common-language.md) |
| 2 | Conduct well-architected reviews early and often, not just... | MEDIUM | [`well-architected-conduct-well-architected-reviews-early-and-often-not-just.md`](well-architected-conduct-well-architected-reviews-early-and-often-not-just.md) |
| 3 | Prioritize the pillars that matter most for your workload (e | CRITICAL | [`well-architected-prioritize-the-pillars-that-matter-most-for-your-workload-e.md`](well-architected-prioritize-the-pillars-that-matter-most-for-your-workload-e.md) |
| 4 | Leverage the cloud provider's native review tooling to... | MEDIUM | [`well-architected-leverage-the-cloud-provider-s-native-review-tooling-to.md`](well-architected-leverage-the-cloud-provider-s-native-review-tooling-to.md) |
| 5 | Document all tradeoff decisions in Architecture Decision... | MEDIUM | [`well-architected-document-all-tradeoff-decisions-in-architecture-decision.md`](well-architected-document-all-tradeoff-decisions-in-architecture-decision.md) |
| 6 | Remember that well-architected is aspirational -- no... | MEDIUM | [`well-architected-remember-that-well-architected-is-aspirational-no.md`](well-architected-remember-that-well-architected-is-aspirational-no.md) |
| 7 | When working across clouds (multi-cloud or migration), use... | HIGH | [`well-architected-when-working-across-clouds-multi-cloud-or-migration-use.md`](well-architected-when-working-across-clouds-multi-cloud-or-migration-use.md) |
architecture/well-architected/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: well-architected, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
architecture/well-architected/rules/well-architected-conduct-well-architected-reviews-early-and-often-not-just.md
---
title: "Conduct well-architected reviews early and often, not just..."
impact: MEDIUM
impactDescription: "general best practice"
tags: well-architected, dev, architecture, well-architected-reviews, cloud-architecture-evaluation, reliabilitysecuritycostperformance-pillar-analysis
---
## Conduct well-architected reviews early and often, not just...
Conduct well-architected reviews early and often, not just before launch.
architecture/well-architected/rules/well-architected-document-all-tradeoff-decisions-in-architecture-decision.md
---
title: "Document all tradeoff decisions in Architecture Decision..."
impact: MEDIUM
impactDescription: "general best practice"
tags: well-architected, dev, architecture, well-architected-reviews, cloud-architecture-evaluation, reliabilitysecuritycostperformance-pillar-analysis
---
## Document all tradeoff decisions in Architecture Decision...
Document all tradeoff decisions in Architecture Decision Records.
architecture/well-architected/rules/well-architected-leverage-the-cloud-provider-s-native-review-tooling-to.md
---
title: "Leverage the cloud provider's native review tooling to..."
impact: MEDIUM
impactDescription: "general best practice"
tags: well-architected, dev, architecture, well-architected-reviews, cloud-architecture-evaluation, reliabilitysecuritycostperformance-pillar-analysis
---
## Leverage the cloud provider's native review tooling to...
Leverage the cloud provider's native review tooling to structure the assessment.
architecture/well-architected/rules/well-architected-prioritize-the-pillars-that-matter-most-for-your-workload-e.md
---
title: "Prioritize the pillars that matter most for your workload (e"
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: well-architected, dev, architecture, well-architected-reviews, cloud-architecture-evaluation, reliabilitysecuritycostperformance-pillar-analysis
---
## Prioritize the pillars that matter most for your workload (e
Prioritize the pillars that matter most for your workload (e.g., a financial system prioritizes Security and Reliability; a data pipeline prioritizes Performance and Cost).
architecture/well-architected/rules/well-architected-remember-that-well-architected-is-aspirational-no.md
---
title: "Remember that well-architected is aspirational -- no..."
impact: MEDIUM
impactDescription: "general best practice"
tags: well-architected, dev, architecture, well-architected-reviews, cloud-architecture-evaluation, reliabilitysecuritycostperformance-pillar-analysis
---
## Remember that well-architected is aspirational -- no...
Remember that well-architected is aspirational -- no workload scores perfectly on every pillar. The goal is continuous improvement.
architecture/well-architected/rules/well-architected-use-the-well-architected-framework-as-a-common-language.md
---
title: "Use the well-architected framework as a common language..."
impact: MEDIUM
impactDescription: "general best practice"
tags: well-architected, dev, architecture, well-architected-reviews, cloud-architecture-evaluation, reliabilitysecuritycostperformance-pillar-analysis
---
## Use the well-architected framework as a common language...
Use the well-architected framework as a common language between architects, developers, and stakeholders -- not as a compliance checklist.
architecture/well-architected/rules/well-architected-when-working-across-clouds-multi-cloud-or-migration-use.md
---
title: "When working across clouds (multi-cloud or migration), use..."
impact: HIGH
impactDescription: "significant quality or reliability improvement"
tags: well-architected, dev, architecture, well-architected-reviews, cloud-architecture-evaluation, reliabilitysecuritycostperformance-pillar-analysis
---
## When working across clouds (multi-cloud or migration), use...
When working across clouds (multi-cloud or migration), use this cross-cloud comparison to map equivalent concerns and avoid gaps.
architecture/well-architected/SKILL.md
---
name: well-architected
description: |
Cloud well-architected frameworks from AWS, Azure, and GCP -- covering pillars, design principles, review processes, and cross-cloud comparison for building reliable, secure, cost-effective cloud workloads.
USE FOR: well-architected reviews, cloud architecture evaluation, reliability/security/cost/performance pillar analysis, cross-cloud architecture comparison
DO NOT USE FOR: cloud infrastructure provisioning (use iac/terraform, iac/bicep, etc.), specific cloud services, microservice patterns (use microservices)
license: MIT
metadata:
displayName: "Well-Architected Frameworks"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "AWS Well-Architected Framework"
url: "https://docs.aws.amazon.com/wellarchitected/latest/framework/welcome.html"
- title: "Microsoft Azure Well-Architected Framework"
url: "https://learn.microsoft.com/en-us/azure/well-architected/"
- title: "Google Cloud Architecture Framework"
url: "https://cloud.google.com/architecture/framework"
---
# Cloud Well-Architected Frameworks
## Overview
The major cloud providers each publish a Well-Architected Framework -- a set of pillars, design principles, and best practices for building reliable, secure, performant, and cost-effective workloads in the cloud. While the terminology and organization differ, the core concerns are remarkably consistent across all three.
This skill covers all three frameworks in a unified view, enabling cross-cloud comparison and provider-agnostic architecture reasoning.
## Cross-Cloud Pillar Comparison
| Concern | AWS (6 Pillars) | Azure (5 Pillars) | GCP (6 Pillars) |
|---------|-----------------|-------------------|-----------------|
| **Operations** | Operational Excellence | Operational Excellence | Operational Excellence |
| **Security** | Security | Security | Security, Privacy & Compliance |
| **Reliability** | Reliability | Reliability | Reliability |
| **Performance** | Performance Efficiency | Performance Efficiency | Performance Optimization |
| **Cost** | Cost Optimization | Cost Optimization | Cost Optimization |
| **Sustainability** | Sustainability | -- | -- |
| **System Design** | -- | -- | System Design |
**Key observation:** All three frameworks agree on the five core concerns (operations, security, reliability, performance, cost). AWS adds Sustainability; GCP adds System Design as an explicit pillar; Azure covers both implicitly within its five pillars.
---
## AWS Well-Architected Framework (6 Pillars)
### 1. Operational Excellence
Design, run, and monitor systems to deliver business value and continually improve processes and procedures.
**Key principles:**
- Perform operations as code (Infrastructure as Code)
- Make frequent, small, reversible changes
- Refine operations procedures frequently
- Anticipate failure; learn from all operational events
- Use managed services to reduce operational burden
### 2. Security
Protect data, systems, and assets through risk assessments, security controls, and automated security best practices.
**Key principles:**
- Implement a strong identity foundation (least privilege, IAM)
- Enable traceability (logging, auditing, monitoring)
- Apply security at all layers (edge, VPC, subnet, instance, OS, application)
- Automate security best practices
- Protect data in transit and at rest
- Keep people away from data (reduce direct access)
- Prepare for security events (incident response runbooks)
### 3. Reliability
Ensure a workload can recover from failures and meet demand through proper planning and design.
**Key principles:**
- Automatically recover from failure
- Test recovery procedures
- Scale horizontally to increase aggregate availability
- Stop guessing capacity (use auto-scaling)
- Manage change through automation
### 4. Performance Efficiency
Use computing resources efficiently and maintain that efficiency as demand changes and technologies evolve.
**Key principles:**
- Democratize advanced technologies (use managed services)
- Go global in minutes (multi-region)
- Use serverless architectures where possible
- Experiment more often
- Consider mechanical sympathy (understand how services are consumed)
### 5. Cost Optimization
Avoid unnecessary costs and understand where money is being spent.
**Key principles:**
- Implement cloud financial management
- Adopt a consumption model (pay for what you use)
- Measure overall efficiency
- Stop spending money on undifferentiated heavy lifting
- Analyze and attribute expenditure
### 6. Sustainability
Minimize environmental impact of cloud workloads.
**Key principles:**
- Understand your impact
- Establish sustainability goals
- Maximize utilization
- Anticipate and adopt new, more efficient offerings
- Use managed services (shared infrastructure is more efficient)
- Reduce downstream impact of your cloud workloads
---
## Azure Well-Architected Framework (5 Pillars)
### 1. Reliability
Ensure the application meets its availability commitments through resiliency and recovery design.
**Key principles:**
- Design for business requirements (define SLA/SLO/SLI)
- Design for failure (assume everything can fail)
- Observe application health (monitoring, alerting)
- Drive automation (minimize human error)
- Design for self-healing
- Design for scale-out
### 2. Security
Protect the confidentiality, integrity, and availability of the application and its data.
**Key principles:**
- Plan resources and how to harden them
- Automate and use least privilege
- Classify and encrypt data
- Guard with identity management (Zero Trust)
- Monitor security for the entire system
- Secure the supply chain
### 3. Cost Optimization
Balance business goals with budget to create a cost-effective workload while avoiding waste.
**Key principles:**
- Develop cost-management discipline
- Design with a cost-efficiency mindset
- Design for usage optimization (right-size, auto-scale)
- Continuously monitor and optimize
### 4. Operational Excellence
Reduce issues in production by building holistic observability and automated processes.
**Key principles:**
- Embrace DevOps culture
- Establish development standards (IaC, CI/CD)
- Evolve operations with observability
- Deploy with confidence (progressive rollout, rollback)
- Automate for efficiency
- Adopt safe deployment practices
### 5. Performance Efficiency
Efficiently scale your workload to meet demand without over-provisioning or under-provisioning.
**Key principles:**
- Negotiate realistic performance targets (SLAs/SLOs)
- Design to meet capacity requirements
- Achieve and sustain performance
- Improve efficiency through optimization
- Monitor and collect data to measure performance
---
## GCP Architecture Framework (6 Pillars)
### 1. System Design
Design systems that meet functional and non-functional requirements using cloud-native patterns.
**Key principles:**
- Design for change (loosely coupled components)
- Design for automation
- Design for managed services
- Design for portability where appropriate
- Design for observability
### 2. Operational Excellence
Deploy, operate, and monitor systems efficiently with minimal manual intervention.
**Key principles:**
- Automate deployments (CI/CD)
- Practice infrastructure as code
- Monitor and alert on SLIs
- Conduct game days and chaos engineering
- Implement progressive rollouts
### 3. Security, Privacy & Compliance
Protect data and systems, maintain privacy, and meet compliance requirements.
**Key principles:**
- Leverage shared responsibility model
- Apply defense in depth
- Automate security controls
- Classify data by sensitivity
- Implement identity federation and least privilege
- Manage compliance as code
### 4. Reliability
Design and operate a resilient, highly available service that meets availability targets.
**Key principles:**
- Define and measure SLOs/SLIs
- Build redundancy to handle failures
- Design for graceful degradation
- Implement health monitoring and automated remediation
- Test for reliability (disaster recovery, chaos engineering)
### 5. Cost Optimization
Manage and optimize costs while maintaining performance and reliability.
**Key principles:**
- Identify cost drivers
- Right-size and auto-scale resources
- Use committed use discounts and sustained use discounts
- Monitor and forecast costs
- Build a cost-aware culture
### 6. Performance Optimization
Design, validate, and tune resources for optimal performance.
**Key principles:**
- Define performance requirements early
- Benchmark and load-test
- Optimize at the application and infrastructure layers
- Use caching and CDNs
- Monitor performance continuously
---
## Well-Architected Review Process
A Well-Architected Review (WAR) is a structured assessment of a workload against the framework's pillars. All three clouds provide review tooling:
| Cloud | Tool | How It Works |
|-------|------|-------------|
| **AWS** | AWS Well-Architected Tool | Answer questions per pillar; generates findings and improvement plan |
| **Azure** | Azure Well-Architected Review (online assessment) | Self-service questionnaire; generates recommendations |
| **GCP** | Architecture Framework checklists + Cloud Architecture Center | Checklist-driven review; reference architectures |
### Review Steps
1. **Scope the workload** -- Define the boundary of what is being reviewed (a single application, a platform, a service).
2. **Assemble the team** -- Include architects, developers, operations, security, and finance.
3. **Walk through each pillar** -- Answer the framework's questions honestly. Identify gaps.
4. **Prioritize findings** -- Rank by business impact and effort. Focus on high-risk, high-impact items first.
5. **Create an improvement plan** -- Assign owners, set deadlines, track progress.
6. **Schedule regular reviews** -- Architecture is not a one-time activity. Review quarterly or after major changes.
### Review Frequency
| Trigger | Action |
|---------|--------|
| New workload launch | Full review before production |
| Major architecture change | Review affected pillars |
| Quarterly cadence | Lightweight review of all pillars |
| Incident or outage | Review Reliability and Operational Excellence pillars |
| Cost spike | Review Cost Optimization pillar |
## Pillar Tensions and Tradeoffs
The pillars are inherently in tension. Optimizing one often increases costs or complexity in another:
| Tradeoff | Example |
|----------|---------|
| Reliability vs. Cost | Multi-region deployment increases availability but doubles infrastructure cost |
| Security vs. Performance | Encryption at rest and in transit adds latency |
| Performance vs. Cost | Over-provisioning ensures headroom but wastes money |
| Operational Excellence vs. Speed | Comprehensive CI/CD and observability take time to set up but pay off long-term |
| Sustainability vs. Performance | Right-sizing reduces waste but may reduce performance headroom |
**Key principle:** Make tradeoffs explicitly. Document which pillars are prioritized and why (use Architecture Decision Records -- see `specs/documentation/adr`).
## Best Practices
- Use the well-architected framework as a common language between architects, developers, and stakeholders -- not as a compliance checklist.
- Conduct well-architected reviews early and often, not just before launch.
- Prioritize the pillars that matter most for your workload (e.g., a financial system prioritizes Security and Reliability; a data pipeline prioritizes Performance and Cost).
- Leverage the cloud provider's native review tooling to structure the assessment.
- Document all tradeoff decisions in Architecture Decision Records.
- Remember that well-architected is aspirational -- no workload scores perfectly on every pillar. The goal is continuous improvement.
- When working across clouds (multi-cloud or migration), use this cross-cloud comparison to map equivalent concerns and avoid gaps.
backend/AGENTS.md
# Backend Architecture
## Overview
Backend architecture encompasses the server-side decisions that determine how a system stores data, exposes functionality, handles security, and scales under load. The choices made at this level -- API style, database type, caching strategy, authentication mechanism -- ripple through every layer of the application and are difficult to change once established.
This skill provides a decision-making framework drawn from Martin Kleppmann's *Designing Data-Intensive Applications* and industry-proven patterns for building reliable, scalable, and maintainable backend systems.
## Knowledge Map
```
┌─────────────────────────────────────────────────────────────────┐
│ API Layer │
│ REST, GraphQL, gRPC, WebSocket │
│ → How clients communicate with the backend │
├─────────────────────────────────────────────────────────────────┤
│ Data Storage │ Caching │
│ Relational, Document, Graph, │ In-memory, Distributed, │
│ Key-Value, Time-Series │ CDN, HTTP caching │
│ → How data is persisted │ → How hot data is served │
├─────────────────────────────────────────────────────────────────┤
│ Authentication & Authorization │ Background Processing │
│ OAuth 2.0, JWT, RBAC, ABAC, │ Job queues, schedulers, │
│ Multi-tenancy │ event-driven workers │
├─────────────────────────────────────────────────────────────────┤
│ Rate Limiting & Throttling │ Observability │
│ Token bucket, sliding window, │ Logging, metrics, tracing, │
│ API quotas, circuit breakers │ health checks, alerting │
└─────────────────────────────────────────────────────────────────┘
```
## Choosing an API Style
| Criterion | REST | GraphQL | gRPC | WebSocket |
|-----------|------|---------|------|-----------|
| **Best for** | CRUD resources, public APIs | Flexible queries, mobile clients | Internal microservices, high throughput | Real-time bidirectional communication |
| **Data format** | JSON (typically) | JSON | Protobuf (binary) | Any (JSON, binary) |
| **Contract** | OpenAPI / Swagger | Schema (SDL) | .proto files | No standard schema |
| **Caching** | HTTP caching (excellent) | Harder (POST-based) | No HTTP caching | Not cacheable |
| **Streaming** | SSE (server-only) | Subscriptions | Bidirectional streaming | Full-duplex native |
| **Browser support** | Native | Native | Requires gRPC-Web proxy | Native |
| **Learning curve** | Low | Medium | Medium-High | Low-Medium |
| **Over/under-fetching** | Common problem | Solved by design | Defined per RPC | N/A |
| **Tooling maturity** | Excellent | Good | Good (growing) | Moderate |
**Decision heuristic:**
- Default to **REST** for public APIs and simple CRUD services.
- Choose **GraphQL** when clients need flexible, aggregated queries across multiple resources (especially mobile).
- Choose **gRPC** for internal service-to-service communication where latency and throughput matter.
- Choose **WebSocket** when you need real-time, bidirectional data flow (chat, live dashboards, collaborative editing).
- Many systems combine styles: REST for public API, gRPC internally, WebSocket for real-time features.
## Choosing a Database Type
| Criterion | Relational (SQL) | Document (NoSQL) | Graph | Key-Value | Time-Series |
|-----------|------------------|-------------------|-------|-----------|-------------|
| **Best for** | Structured data, complex joins, ACID transactions | Flexible schemas, nested data, rapid iteration | Highly connected data, relationship traversal | Simple lookups, caching, session storage | Metrics, IoT, logs, financial ticks |
| **Examples** | PostgreSQL, MySQL, SQL Server | MongoDB, CouchDB, DynamoDB | Neo4j, Amazon Neptune, ArangoDB | Redis, Memcached, DynamoDB | InfluxDB, TimescaleDB, Prometheus |
| **Schema** | Strict (schema-on-write) | Flexible (schema-on-read) | Property graph / RDF | Schema-free | Tag + field model |
| **Scaling** | Vertical (horizontal with sharding) | Horizontal (built-in) | Vertical (some horizontal) | Horizontal (built-in) | Horizontal (built-in) |
| **Transactions** | Full ACID | Limited (document-level) | Varies by product | None (typically) | None (typically) |
| **Query language** | SQL | Vendor-specific (MQL, etc.) | Cypher, Gremlin, SPARQL | GET/SET commands | InfluxQL, Flux, SQL |
| **Joins** | Excellent | Poor (application-level) | Excellent (traversals) | None | Limited |
**Decision heuristic:**
- Default to **relational** (PostgreSQL) when data is structured and relationships matter.
- Choose **document** when schema flexibility and developer velocity are priorities, and joins are rare.
- Choose **graph** when the primary queries traverse relationships (social networks, recommendations, fraud detection).
- Choose **key-value** for caching, sessions, and simple lookup-by-key workloads.
- Choose **time-series** for append-heavy, time-stamped data with downsampling and retention needs.
- Polyglot persistence is common: use the right database for each bounded context.
## Backend Architecture Concerns
### Rate Limiting & Throttling
Protect backend services from abuse and overload:
- **Token Bucket** -- allows bursts up to a configured capacity, refills at a steady rate.
- **Sliding Window** -- counts requests in a rolling time window for smoother limiting.
- **Fixed Window** -- simple counter per time window (risk of burst at window boundaries).
- **Leaky Bucket** -- processes requests at a constant rate, queuing excess.
- Implement at the API gateway layer for consistency across services.
### Background Processing
Offload long-running or non-urgent work from the request/response cycle:
- **Job queues** (Sidekiq, Celery, Hangfire, BullMQ) -- enqueue work, process asynchronously.
- **Scheduled jobs** (cron, Quartz, Hangfire recurring) -- time-triggered processing.
- **Event-driven workers** -- react to domain events from a message broker.
- **Batch processing** -- periodic bulk operations (ETL, report generation).
- Always design for idempotency -- workers may process the same job more than once.
### Observability
The three pillars of observability, plus health monitoring:
- **Logging** -- structured logs (JSON) with correlation IDs for request tracing.
- **Metrics** -- counters, gauges, histograms (request rate, error rate, latency percentiles).
- **Distributed Tracing** -- end-to-end trace across services (OpenTelemetry, Jaeger, Zipkin).
- **Health checks** -- liveness (is the process running?) and readiness (can it serve traffic?).
- **Alerting** -- thresholds on key metrics (error rate > 1%, p99 latency > 500ms).
## Canonical Reference
- *Designing Data-Intensive Applications* by Martin Kleppmann -- the definitive guide to data storage, replication, partitioning, encoding, and distributed system trade-offs. Essential reading for any backend architect.
## Sub-Skills
- `dev/backend/data-modeling` -- Data modeling and database architecture patterns
- `dev/backend/api-design` -- API design patterns for REST, GraphQL, gRPC, WebSocket
- `dev/backend/caching` -- Caching strategies and patterns
- `dev/backend/authentication` -- Authentication and authorization patterns
## Best Practices
- Start with a monolith and extract services only when complexity demands it -- premature microservices add coordination cost without proportional benefit.
- Choose boring technology by default. PostgreSQL, Redis, and a well-designed REST API solve the vast majority of backend problems.
- Design for failure: every network call can fail, every database can be slow. Use timeouts, retries with backoff, circuit breakers, and fallbacks.
- Make operations idempotent wherever possible -- especially for writes, background jobs, and event handlers.
- Instrument everything from day one. Adding observability retroactively is far more expensive than building it in.
- Treat API contracts as public commitments: version explicitly, deprecate gracefully, never break existing clients without a migration path.
backend/api-design/AGENTS.md
# API Design Patterns
## Overview
API design determines how clients interact with backend services. A well-designed API is intuitive, consistent, evolvable, and resilient. This skill covers the four major API styles -- REST, GraphQL, gRPC, and WebSocket -- along with cross-cutting concerns like versioning, pagination, rate limiting, and idempotency.
## REST API Design
### Resource Naming Conventions
```
GET /users → List users
POST /users → Create a user
GET /users/{id} → Get a specific user
PUT /users/{id} → Replace a user
PATCH /users/{id} → Partially update a user
DELETE /users/{id} → Delete a user
GET /users/{id}/orders → List orders for a user (sub-resource)
```
**Rules:**
- Use **nouns** (not verbs) for resource names: `/users` not `/getUsers`.
- Use **plural** nouns: `/users` not `/user`.
- Use **kebab-case** for multi-word resources: `/order-items` not `/orderItems`.
- Nest sub-resources only one level deep. Beyond that, promote to a top-level resource.
### HTTP Methods & Status Codes
| Method | Semantics | Idempotent | Safe |
|--------|-----------|------------|------|
| GET | Read a resource | Yes | Yes |
| POST | Create a resource / trigger action | No | No |
| PUT | Replace a resource entirely | Yes | No |
| PATCH | Partially update a resource | No* | No |
| DELETE | Remove a resource | Yes | No |
*PATCH can be made idempotent with careful design (e.g., JSON Merge Patch).
| Status Code | When to Use |
|-------------|-------------|
| 200 OK | Successful GET, PUT, PATCH |
| 201 Created | Successful POST (include `Location` header) |
| 204 No Content | Successful DELETE |
| 400 Bad Request | Malformed request body or parameters |
| 401 Unauthorized | Missing or invalid authentication |
| 403 Forbidden | Authenticated but insufficient permissions |
| 404 Not Found | Resource does not exist |
| 409 Conflict | State conflict (e.g., duplicate, version mismatch) |
| 422 Unprocessable Entity | Validation errors on well-formed request |
| 429 Too Many Requests | Rate limit exceeded (include `Retry-After`) |
| 500 Internal Server Error | Unhandled server error |
### HATEOAS (Hypermedia As The Engine Of Application State)
Include links in responses so clients can discover available actions:
```json
{
"id": "usr_42",
"name": "Alice",
"_links": {
"self": { "href": "/users/usr_42" },
"orders": { "href": "/users/usr_42/orders" },
"deactivate": { "href": "/users/usr_42/deactivate", "method": "POST" }
}
}
```
### Pagination
| Strategy | Pros | Cons |
|----------|------|------|
| **Offset-based** (`?offset=20&limit=10`) | Simple, supports jumping to page N | Inconsistent with concurrent writes; slow at large offsets |
| **Cursor-based** (`?cursor=abc123&limit=10`) | Consistent during writes; performant at any depth | Cannot jump to arbitrary page; cursor is opaque |
**Recommendation:** Use cursor-based pagination for any dataset that changes frequently or grows large. Use offset-based only for small, static datasets or when page-jumping is a hard requirement.
```json
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTAwfQ==",
"has_more": true
}
}
```
### Filtering & Sorting
```
GET /orders?status=shipped&created_after=2024-01-01&sort=-created_at&limit=20
```
- Use query parameters for filtering. Prefix sort fields with `-` for descending.
- For complex filtering, consider a structured query parameter: `?filter[status]=shipped&filter[total_gte]=100`.
### Versioning Strategies
| Strategy | Example | Pros | Cons |
|----------|---------|------|------|
| **URL path** | `/v1/users` | Explicit, easy to route | URL pollution, hard to sunset |
| **Header** | `Accept: application/vnd.api+json;version=2` | Clean URLs | Hidden, harder to test in browser |
| **Content negotiation** | `Accept: application/vnd.myapp.v2+json` | RESTful, media-type driven | Complex, less discoverable |
**Recommendation:** URL-path versioning (`/v1/`, `/v2/`) is the most practical for most teams. Use it unless you have strong reasons for header-based versioning.
### Richardson Maturity Model
| Level | Description | Example |
|-------|-------------|---------|
| **0 — The Swamp of POX** | Single URI, single HTTP method (usually POST) | `POST /api` with action in body |
| **1 — Resources** | Multiple URIs, but only POST/GET | `GET /users`, `POST /users` |
| **2 — HTTP Verbs** | Proper use of GET, POST, PUT, DELETE, status codes | `PUT /users/42` returns 200 |
| **3 — Hypermedia Controls** | HATEOAS: responses include links to related actions | Links in response body |
Most production APIs target Level 2. Level 3 (HATEOAS) adds discoverability but increases response size and complexity.
## GraphQL Schema Design
### Schema Example (Schema-First / SDL)
```graphql
type User {
id: ID!
name: String!
email: String!
orders(first: Int, after: String): OrderConnection!
}
type Order {
id: ID!
total: Float!
status: OrderStatus!
items: [OrderItem!]!
}
enum OrderStatus {
PENDING
SHIPPED
DELIVERED
CANCELLED
}
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}
type OrderEdge {
node: Order!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
type Query {
user(id: ID!): User
users(first: Int, after: String): UserConnection!
}
type Mutation {
createOrder(input: CreateOrderInput!): Order!
cancelOrder(id: ID!): Order!
}
type Subscription {
orderStatusChanged(userId: ID!): Order!
}
input CreateOrderInput {
userId: ID!
items: [OrderItemInput!]!
}
```
### The N+1 Problem & DataLoader
```
Query: { users { orders { items } } }
Without DataLoader:
1 query for users
N queries for orders (one per user) ← N+1 problem
M queries for items (one per order)
With DataLoader:
1 query for users
1 batched query for all orders ← solved
1 batched query for all items
```
**DataLoader** batches and caches database lookups within a single request. It collects all keys requested during a single tick of the event loop, then issues a single batched query.
### Schema-First vs. Code-First
| Approach | Tools | Pros | Cons |
|----------|-------|------|------|
| **Schema-first** | Apollo, graphql-tools | Schema is the contract; language-agnostic | Schema and resolvers can drift |
| **Code-first** | Nexus, TypeGraphQL, Strawberry | Type safety, co-located logic | Schema is derived, less portable |
### Federation
For microservices, **Apollo Federation** (or similar) lets each service own part of the graph:
```
Service A owns: User { id, name, email }
Service B owns: User { orders: [Order] } ← extends User
Gateway composes both into a single graph
```
## gRPC Service Design
### Protobuf Service Definition
```protobuf
syntax = "proto3";
package orders.v1;
service OrderService {
// Unary RPC
rpc GetOrder(GetOrderRequest) returns (Order);
// Server streaming
rpc WatchOrderStatus(WatchOrderRequest) returns (stream OrderStatusEvent);
// Client streaming
rpc UploadOrderItems(stream OrderItem) returns (UploadSummary);
// Bidirectional streaming
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
message GetOrderRequest {
string order_id = 1;
}
message Order {
string id = 1;
string user_id = 2;
repeated OrderItem items = 3;
OrderStatus status = 4;
double total = 5;
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_SHIPPED = 2;
ORDER_STATUS_DELIVERED = 3;
}
message OrderItem {
string product_id = 1;
int32 quantity = 2;
double unit_price = 3;
}
```
### Communication Patterns
| Pattern | Use Case | Flow |
|---------|----------|------|
| **Unary** | Standard request-response | Client sends one message, server replies with one message |
| **Server streaming** | Live updates, large result sets | Client sends one message, server streams multiple responses |
| **Client streaming** | File upload, batch ingestion | Client streams multiple messages, server replies once |
| **Bidirectional streaming** | Chat, real-time collaboration | Both sides stream messages independently |
### gRPC Best Practices
- **Deadlines:** Always set deadlines on client calls. Propagate deadlines across service boundaries.
- **Interceptors:** Use interceptors (middleware) for logging, authentication, and metrics.
- **Error codes:** Use standard gRPC status codes (NOT_FOUND, INVALID_ARGUMENT, DEADLINE_EXCEEDED, etc.).
- **gRPC-Web:** For browser clients, use Envoy or grpc-web proxy since browsers do not support HTTP/2 trailers natively.
## WebSocket Protocol Design
### Connection Lifecycle
```
1. Client sends HTTP Upgrade request
2. Server responds with 101 Switching Protocols
3. Full-duplex communication over persistent TCP connection
4. Either side can send frames at any time
5. Close handshake (close frame + acknowledgment)
```
### Design Patterns
| Pattern | Description |
|---------|-------------|
| **Rooms / Channels** | Group connections by topic; broadcast within a room (e.g., `chat:room-42`) |
| **Heartbeat / Ping-Pong** | Periodic ping frames detect dead connections; server or client can initiate |
| **Reconnection with backoff** | Client reconnects on disconnect with exponential backoff + jitter |
| **Message acknowledgment** | Assign IDs to messages; receiver acknowledges; sender retries unacknowledged |
### Message Format Convention
```json
{
"type": "order.status_changed",
"payload": {
"order_id": "ord_123",
"new_status": "shipped"
},
"id": "msg_abc",
"timestamp": "2024-01-15T14:30:00Z"
}
```
## Cross-Cutting API Concerns
### API Gateway Patterns
- **Request routing** -- route by path, header, or method to the correct backend service.
- **Authentication offloading** -- verify tokens at the gateway; pass claims to backends.
- **Rate limiting** -- enforce quotas per client/API key at the gateway.
- **Response caching** -- cache GET responses at the edge.
- **Request/response transformation** -- reshape payloads between external and internal formats.
### Idempotency Keys
For non-idempotent operations (especially payments), clients include a unique `Idempotency-Key` header. The server stores the result keyed by this value and returns the cached result on retry.
```
POST /payments
Idempotency-Key: pay_req_abc123
Content-Type: application/json
{ "amount": 99.99, "currency": "USD" }
```
### OpenAPI / Swagger Documentation
For REST APIs, maintain an OpenAPI specification as the source of truth. Cross-reference **specs** for documentation standards. Generate client SDKs, server stubs, and interactive docs from the spec.
## Best Practices
- Design APIs for the consumer, not the database schema. Resource models should reflect use cases, not table structures.
- Be consistent: once you pick conventions for naming, pagination, error format, and versioning, apply them uniformly across all endpoints.
- Use pagination on every list endpoint from day one. Unpaginated lists become production incidents.
- Prefer cursor-based pagination for any data that changes or grows.
- Always set and propagate deadlines/timeouts. An API call without a timeout is a resource leak waiting to happen.
- Include correlation IDs in every request/response for end-to-end tracing.
- Document your API with OpenAPI (REST) or SDL (GraphQL) and keep the spec in version control alongside the code.
backend/api-design/metadata.json
{
"version": "1.0.0",
"name": "api-design",
"displayName": "API Design Patterns",
"description": "Use when designing APIs — REST endpoints, GraphQL schemas, gRPC services, or WebSocket protocols — including resource naming, versioning, pagination, error handling, and API gateway patterns.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "OpenAPI Specification",
"url": "https://www.openapis.org/"
},
{
"title": "GraphQL Official Documentation",
"url": "https://graphql.org/"
},
{
"title": "gRPC Official Documentation",
"url": "https://grpc.io/"
}
]
}
backend/api-design/README.md
# API Design Patterns
Use when designing APIs — REST endpoints, GraphQL schemas, gRPC services, or WebSocket protocols — including resource naming, versioning, pagination, error handling, and API gateway patterns.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 7 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/backend/api-design
```
## License
MIT
backend/api-design/rules/_sections.md
# API Design Patterns Rules
Best practices and rules for API Design Patterns.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Design APIs for the consumer, not the database schema | MEDIUM | [`api-design-design-apis-for-the-consumer-not-the-database-schema.md`](api-design-design-apis-for-the-consumer-not-the-database-schema.md) |
| 2 | Be consistent | MEDIUM | [`api-design-be-consistent.md`](api-design-be-consistent.md) |
| 3 | Use pagination on every list endpoint from day one | CRITICAL | [`api-design-use-pagination-on-every-list-endpoint-from-day-one.md`](api-design-use-pagination-on-every-list-endpoint-from-day-one.md) |
| 4 | Prefer cursor-based pagination for any data that changes or... | LOW | [`api-design-prefer-cursor-based-pagination-for-any-data-that-changes-or.md`](api-design-prefer-cursor-based-pagination-for-any-data-that-changes-or.md) |
| 5 | Always set and propagate deadlines/timeouts | CRITICAL | [`api-design-always-set-and-propagate-deadlines-timeouts.md`](api-design-always-set-and-propagate-deadlines-timeouts.md) |
| 6 | Include correlation IDs in every request/response for... | MEDIUM | [`api-design-include-correlation-ids-in-every-request-response-for.md`](api-design-include-correlation-ids-in-every-request-response-for.md) |
| 7 | Document your API with OpenAPI (REST) or SDL (GraphQL) and... | MEDIUM | [`api-design-document-your-api-with-openapi-rest-or-sdl-graphql-and.md`](api-design-document-your-api-with-openapi-rest-or-sdl-graphql-and.md) |
backend/api-design/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: api-design, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
backend/api-design/rules/api-design-always-set-and-propagate-deadlines-timeouts.md
---
title: "Always set and propagate deadlines/timeouts"
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: api-design, dev, backend, rest-api-design, graphql-schema-design, grpc-service-definition
---
## Always set and propagate deadlines/timeouts
Always set and propagate deadlines/timeouts. An API call without a timeout is a resource leak waiting to happen.
backend/api-design/rules/api-design-be-consistent.md
---
title: "Be consistent"
impact: MEDIUM
impactDescription: "general best practice"
tags: api-design, dev, backend, rest-api-design, graphql-schema-design, grpc-service-definition
---
## Be consistent
Be consistent: once you pick conventions for naming, pagination, error format, and versioning, apply them uniformly across all endpoints.
backend/api-design/rules/api-design-design-apis-for-the-consumer-not-the-database-schema.md
---
title: "Design APIs for the consumer, not the database schema"
impact: MEDIUM
impactDescription: "general best practice"
tags: api-design, dev, backend, rest-api-design, graphql-schema-design, grpc-service-definition
---
## Design APIs for the consumer, not the database schema
Design APIs for the consumer, not the database schema. Resource models should reflect use cases, not table structures.
backend/api-design/rules/api-design-document-your-api-with-openapi-rest-or-sdl-graphql-and.md
---
title: "Document your API with OpenAPI (REST) or SDL (GraphQL) and..."
impact: MEDIUM
impactDescription: "general best practice"
tags: api-design, dev, backend, rest-api-design, graphql-schema-design, grpc-service-definition
---
## Document your API with OpenAPI (REST) or SDL (GraphQL) and...
Document your API with OpenAPI (REST) or SDL (GraphQL) and keep the spec in version control alongside the code.
backend/api-design/rules/api-design-include-correlation-ids-in-every-request-response-for.md
---
title: "Include correlation IDs in every request/response for..."
impact: MEDIUM
impactDescription: "general best practice"
tags: api-design, dev, backend, rest-api-design, graphql-schema-design, grpc-service-definition
---
## Include correlation IDs in every request/response for...
Include correlation IDs in every request/response for end-to-end tracing.
backend/api-design/rules/api-design-prefer-cursor-based-pagination-for-any-data-that-changes-or.md
---
title: "Prefer cursor-based pagination for any data that changes or..."
impact: LOW
impactDescription: "recommended but situational"
tags: api-design, dev, backend, rest-api-design, graphql-schema-design, grpc-service-definition
---
## Prefer cursor-based pagination for any data that changes or...
Prefer cursor-based pagination for any data that changes or grows.
backend/api-design/rules/api-design-use-pagination-on-every-list-endpoint-from-day-one.md
---
title: "Use pagination on every list endpoint from day one"
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: api-design, dev, backend, rest-api-design, graphql-schema-design, grpc-service-definition
---
## Use pagination on every list endpoint from day one
Use pagination on every list endpoint from day one. Unpaginated lists become production incidents.
backend/api-design/SKILL.md
---
name: api-design
description: |
Use when designing APIs — REST endpoints, GraphQL schemas, gRPC services, or WebSocket protocols — including resource naming, versioning, pagination, error handling, and API gateway patterns.
USE FOR: REST API design, GraphQL schema design, gRPC service definition, WebSocket protocol design, API versioning, pagination strategies, API gateway patterns, idempotency, OpenAPI specifications
DO NOT USE FOR: data storage design (use data-modeling), authentication mechanisms (use authentication), API testing (use testing/api-testing)
license: MIT
metadata:
displayName: "API Design Patterns"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "OpenAPI Specification"
url: "https://www.openapis.org/"
- title: "GraphQL Official Documentation"
url: "https://graphql.org/"
- title: "gRPC Official Documentation"
url: "https://grpc.io/"
---
# API Design Patterns
## Overview
API design determines how clients interact with backend services. A well-designed API is intuitive, consistent, evolvable, and resilient. This skill covers the four major API styles -- REST, GraphQL, gRPC, and WebSocket -- along with cross-cutting concerns like versioning, pagination, rate limiting, and idempotency.
## REST API Design
### Resource Naming Conventions
```
GET /users → List users
POST /users → Create a user
GET /users/{id} → Get a specific user
PUT /users/{id} → Replace a user
PATCH /users/{id} → Partially update a user
DELETE /users/{id} → Delete a user
GET /users/{id}/orders → List orders for a user (sub-resource)
```
**Rules:**
- Use **nouns** (not verbs) for resource names: `/users` not `/getUsers`.
- Use **plural** nouns: `/users` not `/user`.
- Use **kebab-case** for multi-word resources: `/order-items` not `/orderItems`.
- Nest sub-resources only one level deep. Beyond that, promote to a top-level resource.
### HTTP Methods & Status Codes
| Method | Semantics | Idempotent | Safe |
|--------|-----------|------------|------|
| GET | Read a resource | Yes | Yes |
| POST | Create a resource / trigger action | No | No |
| PUT | Replace a resource entirely | Yes | No |
| PATCH | Partially update a resource | No* | No |
| DELETE | Remove a resource | Yes | No |
*PATCH can be made idempotent with careful design (e.g., JSON Merge Patch).
| Status Code | When to Use |
|-------------|-------------|
| 200 OK | Successful GET, PUT, PATCH |
| 201 Created | Successful POST (include `Location` header) |
| 204 No Content | Successful DELETE |
| 400 Bad Request | Malformed request body or parameters |
| 401 Unauthorized | Missing or invalid authentication |
| 403 Forbidden | Authenticated but insufficient permissions |
| 404 Not Found | Resource does not exist |
| 409 Conflict | State conflict (e.g., duplicate, version mismatch) |
| 422 Unprocessable Entity | Validation errors on well-formed request |
| 429 Too Many Requests | Rate limit exceeded (include `Retry-After`) |
| 500 Internal Server Error | Unhandled server error |
### HATEOAS (Hypermedia As The Engine Of Application State)
Include links in responses so clients can discover available actions:
```json
{
"id": "usr_42",
"name": "Alice",
"_links": {
"self": { "href": "/users/usr_42" },
"orders": { "href": "/users/usr_42/orders" },
"deactivate": { "href": "/users/usr_42/deactivate", "method": "POST" }
}
}
```
### Pagination
| Strategy | Pros | Cons |
|----------|------|------|
| **Offset-based** (`?offset=20&limit=10`) | Simple, supports jumping to page N | Inconsistent with concurrent writes; slow at large offsets |
| **Cursor-based** (`?cursor=abc123&limit=10`) | Consistent during writes; performant at any depth | Cannot jump to arbitrary page; cursor is opaque |
**Recommendation:** Use cursor-based pagination for any dataset that changes frequently or grows large. Use offset-based only for small, static datasets or when page-jumping is a hard requirement.
```json
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTAwfQ==",
"has_more": true
}
}
```
### Filtering & Sorting
```
GET /orders?status=shipped&created_after=2024-01-01&sort=-created_at&limit=20
```
- Use query parameters for filtering. Prefix sort fields with `-` for descending.
- For complex filtering, consider a structured query parameter: `?filter[status]=shipped&filter[total_gte]=100`.
### Versioning Strategies
| Strategy | Example | Pros | Cons |
|----------|---------|------|------|
| **URL path** | `/v1/users` | Explicit, easy to route | URL pollution, hard to sunset |
| **Header** | `Accept: application/vnd.api+json;version=2` | Clean URLs | Hidden, harder to test in browser |
| **Content negotiation** | `Accept: application/vnd.myapp.v2+json` | RESTful, media-type driven | Complex, less discoverable |
**Recommendation:** URL-path versioning (`/v1/`, `/v2/`) is the most practical for most teams. Use it unless you have strong reasons for header-based versioning.
### Richardson Maturity Model
| Level | Description | Example |
|-------|-------------|---------|
| **0 — The Swamp of POX** | Single URI, single HTTP method (usually POST) | `POST /api` with action in body |
| **1 — Resources** | Multiple URIs, but only POST/GET | `GET /users`, `POST /users` |
| **2 — HTTP Verbs** | Proper use of GET, POST, PUT, DELETE, status codes | `PUT /users/42` returns 200 |
| **3 — Hypermedia Controls** | HATEOAS: responses include links to related actions | Links in response body |
Most production APIs target Level 2. Level 3 (HATEOAS) adds discoverability but increases response size and complexity.
## GraphQL Schema Design
### Schema Example (Schema-First / SDL)
```graphql
type User {
id: ID!
name: String!
email: String!
orders(first: Int, after: String): OrderConnection!
}
type Order {
id: ID!
total: Float!
status: OrderStatus!
items: [OrderItem!]!
}
enum OrderStatus {
PENDING
SHIPPED
DELIVERED
CANCELLED
}
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
}
type OrderEdge {
node: Order!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
endCursor: String
}
type Query {
user(id: ID!): User
users(first: Int, after: String): UserConnection!
}
type Mutation {
createOrder(input: CreateOrderInput!): Order!
cancelOrder(id: ID!): Order!
}
type Subscription {
orderStatusChanged(userId: ID!): Order!
}
input CreateOrderInput {
userId: ID!
items: [OrderItemInput!]!
}
```
### The N+1 Problem & DataLoader
```
Query: { users { orders { items } } }
Without DataLoader:
1 query for users
N queries for orders (one per user) ← N+1 problem
M queries for items (one per order)
With DataLoader:
1 query for users
1 batched query for all orders ← solved
1 batched query for all items
```
**DataLoader** batches and caches database lookups within a single request. It collects all keys requested during a single tick of the event loop, then issues a single batched query.
### Schema-First vs. Code-First
| Approach | Tools | Pros | Cons |
|----------|-------|------|------|
| **Schema-first** | Apollo, graphql-tools | Schema is the contract; language-agnostic | Schema and resolvers can drift |
| **Code-first** | Nexus, TypeGraphQL, Strawberry | Type safety, co-located logic | Schema is derived, less portable |
### Federation
For microservices, **Apollo Federation** (or similar) lets each service own part of the graph:
```
Service A owns: User { id, name, email }
Service B owns: User { orders: [Order] } ← extends User
Gateway composes both into a single graph
```
## gRPC Service Design
### Protobuf Service Definition
```protobuf
syntax = "proto3";
package orders.v1;
service OrderService {
// Unary RPC
rpc GetOrder(GetOrderRequest) returns (Order);
// Server streaming
rpc WatchOrderStatus(WatchOrderRequest) returns (stream OrderStatusEvent);
// Client streaming
rpc UploadOrderItems(stream OrderItem) returns (UploadSummary);
// Bidirectional streaming
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
message GetOrderRequest {
string order_id = 1;
}
message Order {
string id = 1;
string user_id = 2;
repeated OrderItem items = 3;
OrderStatus status = 4;
double total = 5;
}
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_SHIPPED = 2;
ORDER_STATUS_DELIVERED = 3;
}
message OrderItem {
string product_id = 1;
int32 quantity = 2;
double unit_price = 3;
}
```
### Communication Patterns
| Pattern | Use Case | Flow |
|---------|----------|------|
| **Unary** | Standard request-response | Client sends one message, server replies with one message |
| **Server streaming** | Live updates, large result sets | Client sends one message, server streams multiple responses |
| **Client streaming** | File upload, batch ingestion | Client streams multiple messages, server replies once |
| **Bidirectional streaming** | Chat, real-time collaboration | Both sides stream messages independently |
### gRPC Best Practices
- **Deadlines:** Always set deadlines on client calls. Propagate deadlines across service boundaries.
- **Interceptors:** Use interceptors (middleware) for logging, authentication, and metrics.
- **Error codes:** Use standard gRPC status codes (NOT_FOUND, INVALID_ARGUMENT, DEADLINE_EXCEEDED, etc.).
- **gRPC-Web:** For browser clients, use Envoy or grpc-web proxy since browsers do not support HTTP/2 trailers natively.
## WebSocket Protocol Design
### Connection Lifecycle
```
1. Client sends HTTP Upgrade request
2. Server responds with 101 Switching Protocols
3. Full-duplex communication over persistent TCP connection
4. Either side can send frames at any time
5. Close handshake (close frame + acknowledgment)
```
### Design Patterns
| Pattern | Description |
|---------|-------------|
| **Rooms / Channels** | Group connections by topic; broadcast within a room (e.g., `chat:room-42`) |
| **Heartbeat / Ping-Pong** | Periodic ping frames detect dead connections; server or client can initiate |
| **Reconnection with backoff** | Client reconnects on disconnect with exponential backoff + jitter |
| **Message acknowledgment** | Assign IDs to messages; receiver acknowledges; sender retries unacknowledged |
### Message Format Convention
```json
{
"type": "order.status_changed",
"payload": {
"order_id": "ord_123",
"new_status": "shipped"
},
"id": "msg_abc",
"timestamp": "2024-01-15T14:30:00Z"
}
```
## Cross-Cutting API Concerns
### API Gateway Patterns
- **Request routing** -- route by path, header, or method to the correct backend service.
- **Authentication offloading** -- verify tokens at the gateway; pass claims to backends.
- **Rate limiting** -- enforce quotas per client/API key at the gateway.
- **Response caching** -- cache GET responses at the edge.
- **Request/response transformation** -- reshape payloads between external and internal formats.
### Idempotency Keys
For non-idempotent operations (especially payments), clients include a unique `Idempotency-Key` header. The server stores the result keyed by this value and returns the cached result on retry.
```
POST /payments
Idempotency-Key: pay_req_abc123
Content-Type: application/json
{ "amount": 99.99, "currency": "USD" }
```
### OpenAPI / Swagger Documentation
For REST APIs, maintain an OpenAPI specification as the source of truth. Cross-reference **specs** for documentation standards. Generate client SDKs, server stubs, and interactive docs from the spec.
## Best Practices
- Design APIs for the consumer, not the database schema. Resource models should reflect use cases, not table structures.
- Be consistent: once you pick conventions for naming, pagination, error format, and versioning, apply them uniformly across all endpoints.
- Use pagination on every list endpoint from day one. Unpaginated lists become production incidents.
- Prefer cursor-based pagination for any data that changes or grows.
- Always set and propagate deadlines/timeouts. An API call without a timeout is a resource leak waiting to happen.
- Include correlation IDs in every request/response for end-to-end tracing.
- Document your API with OpenAPI (REST) or SDL (GraphQL) and keep the spec in version control alongside the code.
backend/authentication/AGENTS.md
# Authentication & Authorization Patterns
## Overview
Authentication (authn) verifies *who* a user is. Authorization (authz) determines *what* they can do. Getting these right is non-negotiable -- a flaw in either can expose user data, enable privilege escalation, or bring regulatory consequences. This skill covers the major authentication flows, token formats, authorization models, multi-tenancy patterns, and security headers needed to build secure backend systems.
## OAuth 2.0 Flows
### Authorization Code + PKCE (Recommended for Most Apps)
The most secure flow for user-facing applications (SPAs, mobile apps, server-rendered apps). PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks.
```
┌──────────┐ ┌──────────────┐
│ Client │──1. Auth request + ─────────>│ Authorization│
│ (Browser/│ code_challenge │ Server │
│ Mobile) │<──2. Redirect with ─────────│ │
│ │ authorization code │ │
│ │──3. Exchange code + ─────────>│ │
│ │ code_verifier │ │
│ │<──4. Access token + ─────────│ │
│ │ refresh token │ │
└──────────┘ └──────────────┘
│ │
│──5. API request with ──────────>┌──────────────┐
│ Bearer access_token │ Resource │
│<──6. Protected resource ────────│ Server │
│ └──────────────┘
```
**Steps:**
1. Client generates a random `code_verifier` and derives `code_challenge = SHA256(code_verifier)`.
2. Client redirects user to authorization server with `code_challenge`.
3. User authenticates and consents. Authorization server redirects back with an authorization code.
4. Client exchanges the code + `code_verifier` for tokens. Server verifies `SHA256(code_verifier) == code_challenge`.
5. Client uses the access token to call APIs.
### Client Credentials (Machine-to-Machine)
For service-to-service communication where no user is involved.
```
Service A ──POST /token──> Authorization Server
client_id +
client_secret
grant_type=client_credentials
Service A <──access_token── Authorization Server
Service A ──Bearer token──> Service B
```
**Use when:** Backend services authenticate to each other. No user context needed.
### Device Code (TV / IoT / CLI)
For devices with limited input capability.
```
1. Device requests a device code and user code from auth server
2. Device displays: "Go to https://auth.example.com/device and enter code: ABCD-1234"
3. User visits URL on their phone/laptop, enters code, authenticates
4. Device polls auth server until user completes authentication
5. Auth server returns access token to device
```
## OpenID Connect (OIDC)
OIDC is an identity layer built on top of OAuth 2.0. It adds:
| Component | Purpose |
|-----------|---------|
| **ID Token** | A JWT containing user identity claims (sub, email, name) -- proves who the user is |
| **UserInfo Endpoint** | `GET /userinfo` returns additional user profile claims |
| **Discovery** | `GET /.well-known/openid-configuration` returns all endpoint URLs, supported scopes, signing algorithms |
| **Standard Scopes** | `openid` (required), `profile`, `email`, `address`, `phone` |
**Key distinction:** OAuth 2.0 alone is for *authorization* (access to resources). OIDC adds *authentication* (identity of the user).
## JWT (JSON Web Token)
### Structure
```
header.payload.signature
Header (base64url):
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-2024-01"
}
Payload (base64url):
{
"iss": "https://auth.example.com",
"sub": "user_42",
"aud": "https://api.example.com",
"exp": 1705312800,
"iat": 1705309200,
"scope": "read:orders write:orders",
"roles": ["admin"],
"tenant_id": "org_acme"
}
Signature:
RS256(base64url(header) + "." + base64url(payload), private_key)
```
### Standard Claims
| Claim | Purpose |
|-------|---------|
| `iss` | Issuer -- who created the token |
| `sub` | Subject -- who the token represents |
| `aud` | Audience -- who the token is intended for |
| `exp` | Expiration time (Unix timestamp) |
| `iat` | Issued at time |
| `nbf` | Not before -- token is not valid before this time |
| `jti` | JWT ID -- unique identifier for the token |
### Access Tokens vs. Refresh Tokens
| Property | Access Token | Refresh Token |
|----------|-------------|---------------|
| **Purpose** | Authorize API requests | Obtain new access tokens |
| **Lifetime** | Short (5-60 minutes) | Long (hours to days) |
| **Stored** | Memory (preferred) or secure cookie | Secure, HttpOnly cookie or secure storage |
| **Sent to** | Resource server (API) | Authorization server only |
| **Revocable** | Difficult (until expiry) | Yes (server-side revocation list) |
### Token Rotation
Refresh token rotation issues a new refresh token with every access token refresh. If a refresh token is used twice, the server assumes the original was stolen and revokes the entire token family.
```
1. Client sends refresh_token_v1 → Server returns access_token + refresh_token_v2
2. Client sends refresh_token_v2 → Server returns access_token + refresh_token_v3
3. Attacker sends refresh_token_v1 → Server detects reuse → revokes ALL tokens for user
```
## Session-Based Authentication
### Server-Side Sessions
```
1. User submits credentials
2. Server validates, creates session record (in DB or Redis)
3. Server sends session ID in a cookie
4. Client sends cookie with every request
5. Server looks up session by ID, retrieves user context
```
### Cookie Security
| Attribute | Purpose | Recommendation |
|-----------|---------|----------------|
| `HttpOnly` | Prevents JavaScript access (XSS mitigation) | Always set |
| `Secure` | Cookie sent only over HTTPS | Always set in production |
| `SameSite=Lax` | Mitigates CSRF for top-level navigations | Default for most apps |
| `SameSite=Strict` | Cookie never sent cross-site | For sensitive operations |
| `Path=/` | Scope the cookie to a path | Set appropriately |
| `Max-Age` / `Expires` | Session duration | Match your session TTL |
### CSRF Protection
- **SameSite cookies** (Lax or Strict) -- primary defense in modern browsers.
- **Synchronizer Token Pattern** -- server generates a random token, embeds in forms, validates on POST.
- **Double Submit Cookie** -- CSRF token in both a cookie and a request header; server verifies they match.
## API Key Authentication
Appropriate for:
- Server-to-server communication where OAuth is overkill.
- Public APIs with usage-based billing (keys identify the caller for rate limiting and billing).
- Development/testing environments.
**Not appropriate for:** User-facing authentication (API keys cannot represent user identity or consent).
**Best practices:**
- Treat API keys as secrets. Hash them in storage (like passwords).
- Support key rotation: allow multiple active keys per client.
- Include the key in a header (`X-API-Key` or `Authorization: Bearer`), never in the URL.
- Scope keys to specific permissions and rate limits.
## RBAC (Role-Based Access Control)
Users are assigned **roles**; roles are granted **permissions**. Users inherit the permissions of their assigned roles.
```
Role Hierarchy Example:
admin
├── manage_users
├── manage_orders
└── viewer (inherits)
├── read_orders
└── read_products
User "Alice" → roles: [admin]
→ effective permissions: [manage_users, manage_orders, read_orders, read_products]
User "Bob" → roles: [viewer]
→ effective permissions: [read_orders, read_products]
```
### Implementation Pattern
```python
# Check permission, not role (more granular and maintainable)
# Bad: if user.role == "admin"
# Good: if user.has_permission("manage_orders")
def require_permission(permission):
def decorator(handler):
def wrapper(request):
if not request.user.has_permission(permission):
raise ForbiddenError()
return handler(request)
return wrapper
return decorator
@require_permission("manage_orders")
def cancel_order(request, order_id):
...
```
**Best for:** Most applications. Simple to understand, implement, and audit.
## ABAC (Attribute-Based Access Control)
Access decisions based on **attributes** of the subject, resource, action, and environment -- evaluated by a **policy engine**.
| Attribute Source | Examples |
|-----------------|----------|
| **Subject** | user.role, user.department, user.clearance_level |
| **Resource** | document.classification, order.owner_id, record.tenant_id |
| **Action** | read, write, delete, approve |
| **Environment** | current_time, ip_address, request.is_internal |
### Policy Example (Pseudocode)
```
PERMIT action=read ON resource=document
WHERE subject.clearance_level >= resource.classification
AND subject.department == resource.department
AND environment.time BETWEEN 08:00 AND 18:00
```
**Best for:** Complex authorization requirements where RBAC role explosion becomes unmanageable (e.g., healthcare, government, multi-tenant SaaS with granular permissions).
## Multi-Tenancy Patterns
| Pattern | Isolation Level | Complexity | Cost |
|---------|----------------|------------|------|
| **Shared database, shared schema** | Row-level (tenant_id column) | Low | Lowest |
| **Shared database, schema-per-tenant** | Schema-level | Medium | Medium |
| **Database-per-tenant** | Full database isolation | High | Highest |
### Row-Level Security (Shared Schema)
```sql
-- PostgreSQL Row-Level Security
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.current_tenant')::uuid);
-- Set tenant context per request
SET app.current_tenant = 'org_acme';
SELECT * FROM orders; -- only returns org_acme's orders
```
**Decision heuristic:**
- **Shared schema + RLS** for most SaaS applications (simplest, cost-effective, sufficient isolation).
- **Schema-per-tenant** when tenants need custom schema extensions or stronger isolation without separate databases.
- **Database-per-tenant** when regulatory, compliance, or contractual requirements mandate full data isolation (e.g., healthcare, finance, government).
## Identity Providers
| Provider | Type | Best For |
|----------|------|----------|
| **Auth0** | Managed (Okta) | SaaS apps, rapid development, extensive social login support |
| **Microsoft Entra ID** (Azure AD) | Managed (Microsoft) | Enterprise apps, Microsoft ecosystem, B2B federation |
| **Amazon Cognito** | Managed (AWS) | AWS-native apps, user pools + federated identity |
| **Keycloak** | Open-source (self-hosted) | Full control, on-premises, custom requirements |
| **Firebase Auth** | Managed (Google) | Mobile-first apps, Google ecosystem, quick prototyping |
**Recommendation:** Use a managed identity provider unless you have strong requirements for self-hosting. Building authentication from scratch is a security liability.
## Security Headers
| Header | Purpose | Recommended Value |
|--------|---------|-------------------|
| **CORS** (`Access-Control-Allow-Origin`) | Controls which origins can call your API | Explicit allowlist (never `*` with credentials) |
| **Content-Security-Policy (CSP)** | Controls what content the browser can load/execute | `default-src 'self'; script-src 'self'` (customize per app) |
| **Strict-Transport-Security (HSTS)** | Forces HTTPS for all future requests | `max-age=31536000; includeSubDomains; preload` |
| **Referrer-Policy** | Controls how much referrer info is sent | `strict-origin-when-cross-origin` |
| **X-Content-Type-Options** | Prevents MIME type sniffing | `nosniff` |
| **X-Frame-Options** | Prevents clickjacking via iframes | `DENY` or `SAMEORIGIN` |
| **Permissions-Policy** | Controls browser features (camera, mic, geolocation) | Restrict to only what your app needs |
### CORS Configuration
```
# Preflight request
OPTIONS /api/orders
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
# Preflight response
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true
```
**Rules:**
- Never use `Access-Control-Allow-Origin: *` when `Access-Control-Allow-Credentials: true`.
- Maintain an explicit allowlist of trusted origins.
- Set `Access-Control-Max-Age` to reduce preflight request overhead.
## Best Practices
- Use a managed identity provider (Auth0, Entra ID, Cognito, Keycloak) instead of building authentication from scratch. Authentication is a security-critical function where the cost of getting it wrong is severe.
- Always use PKCE with the Authorization Code flow -- even for server-side apps. It adds security with no meaningful cost.
- Keep access token lifetimes short (5-15 minutes). Use refresh tokens for longer sessions.
- Check permissions, not roles, in your authorization code. This makes RBAC more granular and decouples business logic from role definitions.
- Implement row-level security or tenant-scoped queries as a defense-in-depth measure -- never rely solely on application-level tenant filtering.
- Set all security headers from day one. Adding HSTS, CSP, and CORS retroactively often breaks existing functionality.
- Store secrets (API keys, client secrets, signing keys) in a secrets manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), never in code or environment variables in plain text.
- Rotate signing keys and refresh tokens regularly. Implement token family revocation for refresh token reuse detection.
backend/authentication/metadata.json
{
"version": "1.0.0",
"name": "authentication",
"displayName": "Authentication & Authorization Patterns",
"description": "Use when designing authentication and authorization systems — OAuth 2.0 flows, JWT handling, session management, RBAC/ABAC models, multi-tenancy patterns, and security header configuration.",
"author": "Tyler-R-Kendrick",
"license": "MIT",
"date": "February 2026",
"compatibility": "claude, copilot, cursor",
"references": [
{
"title": "OAuth 2.0 — RFC 6749",
"url": "https://datatracker.ietf.org/doc/html/rfc6749"
},
{
"title": "OAuth.net — OAuth 2.0",
"url": "https://oauth.net/2/"
},
{
"title": "JSON Web Tokens — jwt.io",
"url": "https://jwt.io/introduction"
}
]
}
backend/authentication/README.md
# Authentication & Authorization Patterns
Use when designing authentication and authorization systems — OAuth 2.0 flows, JWT handling, session management, RBAC/ABAC models, multi-tenancy patterns, and security header configuration.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 8 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/backend/authentication
```
## License
MIT
backend/authentication/rules/_sections.md
# Authentication & Authorization Patterns Rules
Best practices and rules for Authentication & Authorization Patterns.
## Rules
| # | Rule | Impact | File |
|---|------|--------|------|
| 1 | Use a managed identity provider (Auth0, Entra ID, Cognito,... | CRITICAL | [`authentication-use-a-managed-identity-provider-auth0-entra-id-cognito.md`](authentication-use-a-managed-identity-provider-auth0-entra-id-cognito.md) |
| 2 | Always use PKCE with the Authorization Code flow -- even... | CRITICAL | [`authentication-always-use-pkce-with-the-authorization-code-flow-even.md`](authentication-always-use-pkce-with-the-authorization-code-flow-even.md) |
| 3 | Keep access token lifetimes short (5-15 minutes) | MEDIUM | [`authentication-keep-access-token-lifetimes-short-5-15-minutes.md`](authentication-keep-access-token-lifetimes-short-5-15-minutes.md) |
| 4 | Check permissions, not roles, in your authorization code | MEDIUM | [`authentication-check-permissions-not-roles-in-your-authorization-code.md`](authentication-check-permissions-not-roles-in-your-authorization-code.md) |
| 5 | Implement row-level security or tenant-scoped queries as a... | CRITICAL | [`authentication-implement-row-level-security-or-tenant-scoped-queries-as-a.md`](authentication-implement-row-level-security-or-tenant-scoped-queries-as-a.md) |
| 6 | Set all security headers from day one | CRITICAL | [`authentication-set-all-security-headers-from-day-one.md`](authentication-set-all-security-headers-from-day-one.md) |
| 7 | Store secrets (API keys, client secrets, signing keys) in a... | CRITICAL | [`authentication-store-secrets-api-keys-client-secrets-signing-keys-in-a.md`](authentication-store-secrets-api-keys-client-secrets-signing-keys-in-a.md) |
| 8 | Rotate signing keys and refresh tokens regularly | MEDIUM | [`authentication-rotate-signing-keys-and-refresh-tokens-regularly.md`](authentication-rotate-signing-keys-and-refresh-tokens-regularly.md) |
backend/authentication/rules/_template.md
---
title: "Rule Title"
impact: MEDIUM
impactDescription: "brief explanation of why this matters"
tags: authentication, tag2
---
## Rule Title
Brief explanation of the rule and why it matters.
**Incorrect:**
```
<!-- Example of what NOT to do -->
```
**Correct:**
```
<!-- Example of the recommended approach -->
```
**Reference:** [link to documentation or source]
backend/authentication/rules/authentication-always-use-pkce-with-the-authorization-code-flow-even.md
---
title: "Always use PKCE with the Authorization Code flow -- even..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Always use PKCE with the Authorization Code flow -- even...
Always use PKCE with the Authorization Code flow -- even for server-side apps. It adds security with no meaningful cost.
backend/authentication/rules/authentication-check-permissions-not-roles-in-your-authorization-code.md
---
title: "Check permissions, not roles, in your authorization code"
impact: MEDIUM
impactDescription: "general best practice"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Check permissions, not roles, in your authorization code
Check permissions, not roles, in your authorization code. This makes RBAC more granular and decouples business logic from role definitions.
backend/authentication/rules/authentication-implement-row-level-security-or-tenant-scoped-queries-as-a.md
---
title: "Implement row-level security or tenant-scoped queries as a..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Implement row-level security or tenant-scoped queries as a...
Implement row-level security or tenant-scoped queries as a defense-in-depth measure -- never rely solely on application-level tenant filtering.
backend/authentication/rules/authentication-keep-access-token-lifetimes-short-5-15-minutes.md
---
title: "Keep access token lifetimes short (5-15 minutes)"
impact: MEDIUM
impactDescription: "general best practice"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Keep access token lifetimes short (5-15 minutes)
Keep access token lifetimes short (5-15 minutes). Use refresh tokens for longer sessions.
backend/authentication/rules/authentication-rotate-signing-keys-and-refresh-tokens-regularly.md
---
title: "Rotate signing keys and refresh tokens regularly"
impact: MEDIUM
impactDescription: "general best practice"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Rotate signing keys and refresh tokens regularly
Rotate signing keys and refresh tokens regularly. Implement token family revocation for refresh token reuse detection.
backend/authentication/rules/authentication-set-all-security-headers-from-day-one.md
---
title: "Set all security headers from day one"
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Set all security headers from day one
Set all security headers from day one. Adding HSTS, CSP, and CORS retroactively often breaks existing functionality.
backend/authentication/rules/authentication-store-secrets-api-keys-client-secrets-signing-keys-in-a.md
---
title: "Store secrets (API keys, client secrets, signing keys) in a..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Store secrets (API keys, client secrets, signing keys) in a...
Store secrets (API keys, client secrets, signing keys) in a secrets manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), never in code or environment variables in plain text.
backend/authentication/rules/authentication-use-a-managed-identity-provider-auth0-entra-id-cognito.md
---
title: "Use a managed identity provider (Auth0, Entra ID, Cognito,..."
impact: CRITICAL
impactDescription: "essential for correctness or security"
tags: authentication, dev, backend, authentication-design, authorization-models, oauth-20-flows
---
## Use a managed identity provider (Auth0, Entra ID, Cognito,...
Use a managed identity provider (Auth0, Entra ID, Cognito, Keycloak) instead of building authentication from scratch. Authentication is a security-critical function where the cost of getting it wrong is severe.
backend/authentication/SKILL.md
---
name: authentication
description: |
Use when designing authentication and authorization systems — OAuth 2.0 flows, JWT handling, session management, RBAC/ABAC models, multi-tenancy patterns, and security header configuration.
USE FOR: authentication design, authorization models, OAuth 2.0 flows, JWT implementation, session management, RBAC, ABAC, multi-tenancy patterns, identity provider selection, security headers, CORS configuration
DO NOT USE FOR: API endpoint design (use api-design), security scanning/SAST (use testing/static-analysis), infrastructure security (use iac)
license: MIT
metadata:
displayName: "Authentication & Authorization Patterns"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "OAuth 2.0 — RFC 6749"
url: "https://datatracker.ietf.org/doc/html/rfc6749"
- title: "OAuth.net — OAuth 2.0"
url: "https://oauth.net/2/"
- title: "JSON Web Tokens — jwt.io"
url: "https://jwt.io/introduction"
---
# Authentication & Authorization Patterns
## Overview
Authentication (authn) verifies *who* a user is. Authorization (authz) determines *what* they can do. Getting these right is non-negotiable -- a flaw in either can expose user data, enable privilege escalation, or bring regulatory consequences. This skill covers the major authentication flows, token formats, authorization models, multi-tenancy patterns, and security headers needed to build secure backend systems.
## OAuth 2.0 Flows
### Authorization Code + PKCE (Recommended for Most Apps)
The most secure flow for user-facing applications (SPAs, mobile apps, server-rendered apps). PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks.
```
┌──────────┐ ┌──────────────┐
│ Client │──1. Auth request + ─────────>│ Authorization│
│ (Browser/│ code_challenge │ Server │
│ Mobile) │<──2. Redirect with ─────────│ │
│ │ authorization code │ │
│ │──3. Exchange code + ─────────>│ │
│ │ code_verifier │ │
│ │<──4. Access token + ─────────│ │
│ │ refresh token │ │
└──────────┘ └──────────────┘
│ │
│──5. API request with ──────────>┌──────────────┐
│ Bearer access_token │ Resource │
│<──6. Protected resource ────────│ Server │
│ └──────────────┘
```
**Steps:**
1. Client generates a random `code_verifier` and derives `code_challenge = SHA256(code_verifier)`.
2. Client redirects user to authorization server with `code_challenge`.
3. User authenticates and consents. Authorization server redirects back with an authorization code.
4. Client exchanges the code + `code_verifier` for tokens. Server verifies `SHA256(code_verifier) == code_challenge`.
5. Client uses the access token to call APIs.
### Client Credentials (Machine-to-Machine)
For service-to-service communication where no user is involved.
```
Service A ──POST /token──> Authorization Server
client_id +
client_secret
grant_type=client_credentials
Service A <──access_token── Authorization Server
Service A ──Bearer token──> Service B
```
**Use when:** Backend services authenticate to each other. No user context needed.
### Device Code (TV / IoT / CLI)
For devices with limited input capability.
```
1. Device requests a device code and user code from auth server
2. Device displays: "Go to https://auth.example.com/device and enter code: ABCD-1234"
3. User visits URL on their phone/laptop, enters code, authenticates
4. Device polls auth server until user completes authentication
5. Auth server returns access token to device
```
## OpenID Connect (OIDC)
OIDC is an identity layer built on top of OAuth 2.0. It adds:
| Component | Purpose |
|-----------|---------|
| **ID Token** | A JWT containing user identity claims (sub, email, name) -- proves who the user is |
| **UserInfo Endpoint** | `GET /userinfo` returns additional user profile claims |
| **Discovery** | `GET /.well-known/openid-configuration` returns all endpoint URLs, supported scopes, signing algorithms |
| **Standard Scopes** | `openid` (required), `profile`, `email`, `address`, `phone` |
**Key distinction:** OAuth 2.0 alone is for *authorization* (access to resources). OIDC adds *authentication* (identity of the user).
## JWT (JSON Web Token)
### Structure
```
header.payload.signature
Header (base64url):
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-2024-01"
}
Payload (base64url):
{
"iss": "https://auth.example.com",
"sub": "user_42",
"aud": "https://api.example.com",
"exp": 1705312800,
"iat": 1705309200,
"scope": "read:orders write:orders",
"roles": ["admin"],
"tenant_id": "org_acme"
}
Signature:
RS256(base64url(header) + "." + base64url(payload), private_key)
```
### Standard Claims
| Claim | Purpose |
|-------|---------|
| `iss` | Issuer -- who created the token |
| `sub` | Subject -- who the token represents |
| `aud` | Audience -- who the token is intended for |
| `exp` | Expiration time (Unix timestamp) |
| `iat` | Issued at time |
| `nbf` | Not before -- token is not valid before this time |
| `jti` | JWT ID -- unique identifier for the token |
### Access Tokens vs. Refresh Tokens
| Property | Access Token | Refresh Token |
|----------|-------------|---------------|
| **Purpose** | Authorize API requests | Obtain new access tokens |
| **Lifetime** | Short (5-60 minutes) | Long (hours to days) |
| **Stored** | Memory (preferred) or secure cookie | Secure, HttpOnly cookie or secure storage |
| **Sent to** | Resource server (API) | Authorization server only |
| **Revocable** | Difficult (until expiry) | Yes (server-side revocation list) |
### Token Rotation
Refresh token rotation issues a new refresh token with every access token refresh. If a refresh token is used twice, the server assumes the original was stolen and revokes the entire token family.
```
1. Client sends refresh_token_v1 → Server returns access_token + refresh_token_v2
2. Client sends refresh_token_v2 → Server returns access_token + refresh_token_v3
3. Attacker sends refresh_token_v1 → Server detects reuse → revokes ALL tokens for user
```
## Session-Based Authentication
### Server-Side Sessions
```
1. User submits credentials
2. Server validates, creates session record (in DB or Redis)
3. Server sends session ID in a cookie
4. Client sends cookie with every request
5. Server looks up session by ID, retrieves user context
```
### Cookie Security
| Attribute | Purpose | Recommendation |
|-----------|---------|----------------|
| `HttpOnly` | Prevents JavaScript access (XSS mitigation) | Always set |
| `Secure` | Cookie sent only over HTTPS | Always set in production |
| `SameSite=Lax` | Mitigates CSRF for top-level navigations | Default for most apps |
| `SameSite=Strict` | Cookie never sent cross-site | For sensitive operations |
| `Path=/` | Scope the cookie to a path | Set appropriately |
| `Max-Age` / `Expires` | Session duration | Match your session TTL |
### CSRF Protection
- **SameSite cookies** (Lax or Strict) -- primary defense in modern browsers.
- **Synchronizer Token Pattern** -- server generates a random token, embeds in forms, validates on POST.
- **Double Submit Cookie** -- CSRF token in both a cookie and a request header; server verifies they match.
## API Key Authentication
Appropriate for:
- Server-to-server communication where OAuth is overkill.
- Public APIs with usage-based billing (keys identify the caller for rate limiting and billing).
- Development/testing environments.
**Not appropriate for:** User-facing authentication (API keys cannot represent user identity or consent).
**Best practices:**
- Treat API keys as secrets. Hash them in storage (like passwords).
- Support key rotation: allow multiple active keys per client.
- Include the key in a header (`X-API-Key` or `Authorization: Bearer`), never in the URL.
- Scope keys to specific permissions and rate limits.
## RBAC (Role-Based Access Control)
Users are assigned **roles**; roles are granted **permissions**. Users inherit the permissions of their assigned roles.
```
Role Hierarchy Example:
admin
├── manage_users
├── manage_orders
└── viewer (inherits)
├── read_orders
└── read_products
User "Alice" → roles: [admin]
→ effective permissions: [manage_users, manage_orders, read_orders, read_products]
User "Bob" → roles: [viewer]
→ effective permissions: [read_orders, read_products]
```
### Implementation Pattern
```python
# Check permission, not role (more granular and maintainable)
# Bad: if user.role == "admin"
# Good: if user.has_permission("manage_orders")
def require_permission(permission):
def decorator(handler):
def wrapper(request):
if not request.user.has_permission(permission):
raise ForbiddenError()
return handler(request)
return wrapper
return decorator
@require_permission("manage_orders")
def cancel_order(request, order_id):
...
```
**Best for:** Most applications. Simple to understand, implement, and audit.
## ABAC (Attribute-Based Access Control)
Access decisions based on **attributes** of the subject, resource, action, and environment -- evaluated by a **policy engine**.
| Attribute Source | Examples |
|-----------------|----------|
| **Subject** | user.role, user.department, user.clearance_level |
| **Resource** | document.classification, order.owner_id, record.tenant_id |
| **Action** | read, write, delete, approve |
| **Environment** | current_time, ip_address, request.is_internal |
### Policy Example (Pseudocode)
```
PERMIT action=read ON resource=document
WHERE subject.clearance_level >= resource.classification
AND subject.department == resource.department
AND environment.time BETWEEN 08:00 AND 18:00
```
**Best for:** Complex authorization requirements where RBAC role explosion becomes unmanageable (e.g., healthcare, government, multi-tenant SaaS with granular permissions).
## Multi-Tenancy Patterns
| Pattern | Isolation Level | Complexity | Cost |
|---------|----------------|------------|------|
| **Shared database, shared schema** | Row-level (tenant_id column) | Low | Lowest |
| **Shared database, schema-per-tenant** | Schema-level | Medium | Medium |
| **Database-per-tenant** | Full database isolation | High | Highest |
### Row-Level Security (Shared Schema)
```sql
-- PostgreSQL Row-Level Security
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON orders
USING (tenant_id = current_setting('app.current_tenant')::uuid);
-- Set tenant context per request
SET app.current_tenant = 'org_acme';
SELECT * FROM orders; -- only returns org_acme's orders
```
**Decision heuristic:**
- **Shared schema + RLS** for most SaaS applications (simplest, cost-effective, sufficient isolation).
- **Schema-per-tenant** when tenants need custom schema extensions or stronger isolation without separate databases.
- **Database-per-tenant** when regulatory, compliance, or contractual requirements mandate full data isolation (e.g., healthcare, finance, government).
## Identity Providers
| Provider | Type | Best For |
|----------|------|----------|
| **Auth0** | Managed (Okta) | SaaS apps, rapid development, extensive social login support |
| **Microsoft Entra ID** (Azure AD) | Managed (Microsoft) | Enterprise apps, Microsoft ecosystem, B2B federation |
| **Amazon Cognito** | Managed (AWS) | AWS-native apps, user pools + federated identity |
| **Keycloak** | Open-source (self-hosted) | Full control, on-premises, custom requirements |
| **Firebase Auth** | Managed (Google) | Mobile-first apps, Google ecosystem, quick prototyping |
**Recommendation:** Use a managed identity provider unless you have strong requirements for self-hosting. Building authentication from scratch is a security liability.
## Security Headers
| Header | Purpose | Recommended Value |
|--------|---------|-------------------|
| **CORS** (`Access-Control-Allow-Origin`) | Controls which origins can call your API | Explicit allowlist (never `*` with credentials) |
| **Content-Security-Policy (CSP)** | Controls what content the browser can load/execute | `default-src 'self'; script-src 'self'` (customize per app) |
| **Strict-Transport-Security (HSTS)** | Forces HTTPS for all future requests | `max-age=31536000; includeSubDomains; preload` |
| **Referrer-Policy** | Controls how much referrer info is sent | `strict-origin-when-cross-origin` |
| **X-Content-Type-Options** | Prevents MIME type sniffing | `nosniff` |
| **X-Frame-Options** | Prevents clickjacking via iframes | `DENY` or `SAMEORIGIN` |
| **Permissions-Policy** | Controls browser features (camera, mic, geolocation) | Restrict to only what your app needs |
### CORS Configuration
```
# Preflight request
OPTIONS /api/orders
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization, Content-Type
# Preflight response
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true
```
**Rules:**
- Never use `Access-Control-Allow-Origin: *` when `Access-Control-Allow-Credentials: true`.
- Maintain an explicit allowlist of trusted origins.
- Set `Access-Control-Max-Age` to reduce preflight request overhead.
## Best Practices
- Use a managed identity provider (Auth0, Entra ID, Cognito, Keycloak) instead of building authentication from scratch. Authentication is a security-critical function where the cost of getting it wrong is severe.
- Always use PKCE with the Authorization Code flow -- even for server-side apps. It adds security with no meaningful cost.
- Keep access token lifetimes short (5-15 minutes). Use refresh tokens for longer sessions.
- Check permissions, not roles, in your authorization code. This makes RBAC more granular and decouples business logic from role definitions.
- Implement row-level security or tenant-scoped queries as a defense-in-depth measure -- never rely solely on application-level tenant filtering.
- Set all security headers from day one. Adding HSTS, CSP, and CORS retroactively often breaks existing functionality.
- Store secrets (API keys, client secrets, signing keys) in a secrets manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault), never in code or environment variables in plain text.
- Rotate signing keys and refresh tokens regularly. Implement token family revocation for refresh token reuse detection.
backend/caching/AGENTS.md
# Caching Strategies & Patterns
## Overview
Caching is the practice of storing copies of data in a faster storage layer so that future requests for that data are served more quickly. Effective caching can reduce database load by orders of magnitude, cut response latency dramatically, and improve system resilience -- but incorrect caching introduces stale data, consistency bugs, and operational complexity. Choosing the right caching strategy is a critical backend architecture decision.
## Core Caching Patterns
### Cache-Aside (Lazy Loading)
The application manages the cache explicitly. On a read, the application checks the cache first. On a miss, it loads from the database, stores the result in cache, and returns it.
```
Read path:
1. App checks cache for key
2. Cache HIT → return cached value
3. Cache MISS → query database
4. Store result in cache with TTL
5. Return result
Write path:
1. App writes to database
2. App invalidates (deletes) the cache key
```
**Pros:** Simple to implement; cache only contains data that is actually requested; works with any database.
**Cons:** First request always hits the database (cold start); potential for stale data between write and invalidation.
**Best for:** General-purpose caching where the application can tolerate brief staleness.
### Read-Through
The cache sits in front of the database and loads data transparently on a miss. The application always reads from the cache -- it never talks to the database directly for reads.
```
Read path:
1. App reads from cache
2. Cache HIT → return cached value
3. Cache MISS → cache loads from database automatically
4. Cache stores result, returns to app
```
**Pros:** Cleaner application code (no cache miss handling); cache warms itself.
**Cons:** Requires a cache layer that supports read-through (or a wrapper); first request still slow.
**Best for:** Workloads where you want the caching logic decoupled from application code.
### Write-Through
Writes go to the cache first, and the cache synchronously writes to the database before confirming the write to the application.
```
Write path:
1. App writes to cache
2. Cache writes to database synchronously
3. Cache confirms write to app
```
**Pros:** Cache is always consistent with the database; no stale reads after writes.
**Cons:** Higher write latency (two writes in series); cache may contain data that is never read.
**Best for:** Read-heavy workloads where consistency is critical and write volume is moderate.
### Write-Behind (Write-Back)
Writes go to the cache immediately, and the cache asynchronously flushes changes to the database in the background.
```
Write path:
1. App writes to cache
2. Cache confirms write to app immediately
3. Cache asynchronously flushes to database (batched, periodic)
```
**Pros:** Very low write latency; writes can be batched for efficiency; absorbs write spikes.
**Cons:** Risk of data loss if the cache fails before flushing; eventual consistency with the database; complex to implement correctly.
**Best for:** Write-heavy workloads where write latency matters more than durability guarantees (e.g., analytics counters, session updates).
### Write-Around
Writes go directly to the database, bypassing the cache entirely. The cache is populated only on subsequent reads (via cache-aside or read-through).
```
Write path:
1. App writes directly to database (cache not involved)
Read path:
1. App reads from cache
2. Cache MISS → load from database, populate cache
```
**Pros:** Avoids polluting the cache with data that may never be read; simple write path.
**Cons:** Recently written data always misses the cache on first read.
**Best for:** Write-heavy workloads where most written data is rarely read immediately (e.g., log ingestion, audit trails).
## Choosing a Caching Strategy
| Criterion | Cache-Aside | Read-Through | Write-Through | Write-Behind | Write-Around |
|-----------|-------------|--------------|---------------|--------------|--------------|
| **Read latency (after warm)** | Low | Low | Low | Low | Low |
| **Write latency** | Normal (DB only) | Normal (DB only) | Higher (cache + DB) | Very low (cache only) | Normal (DB only) |
| **Consistency** | Eventual | Eventual | Strong | Eventual | Eventual |
| **Data loss risk** | None | None | None | Yes (cache failure) | None |
| **Cache pollution** | Low (demand-filled) | Low (demand-filled) | Higher (all writes cached) | Higher (all writes cached) | Lowest |
| **Implementation complexity** | Low | Medium | Medium | High | Low |
| **Best workload** | General purpose | Read-heavy | Read-heavy + consistency | Write-heavy + low latency | Write-heavy + rarely re-read |
**Decision heuristic:**
- Start with **cache-aside** -- it is the simplest and most widely applicable pattern.
- Use **write-through** when you need strong consistency between cache and database.
- Use **write-behind** when write latency is critical and you can tolerate potential data loss.
- Use **write-around** when most writes are not read back immediately.
- Use **read-through** when you want to keep caching logic out of your application code.
## Cache Invalidation Strategies
Cache invalidation is one of the two hard problems in computer science (along with naming things and off-by-one errors).
| Strategy | Mechanism | Trade-off |
|----------|-----------|-----------|
| **TTL (Time-To-Live)** | Cache entries expire after a fixed duration | Simple; data can be stale up to TTL; good baseline |
| **Event-based** | Invalidate cache when a domain event fires (e.g., `order.updated`) | Near-real-time consistency; requires event infrastructure |
| **Version-based** | Cache key includes a version number; bump version on write | No stale reads; requires version tracking |
| **Tag-based** | Associate cache entries with tags; invalidate all entries with a tag | Good for related data; supported by some cache frameworks |
**Recommendation:** Use TTL as a safety net on every cache entry (even with event-based invalidation). This ensures that stale data eventually expires even if an invalidation event is lost.
## Cache Stampede / Thundering Herd
When a popular cache key expires, many concurrent requests simultaneously miss the cache and hit the database, potentially overwhelming it.
### Solutions
| Solution | How It Works |
|----------|--------------|
| **Mutex / distributed lock** | First request that misses acquires a lock and rebuilds the cache; other requests wait or get stale data |
| **Probabilistic early expiration** | Each request has a small probability of refreshing the cache before TTL expires, spreading the refresh load |
| **Stale-while-revalidate** | Serve the stale value while asynchronously refreshing in the background |
| **Pre-warming** | Proactively refresh cache entries before they expire (scheduled or event-triggered) |
```python
# Probabilistic early expiration (XFetch algorithm)
import random, time
def get_with_early_expiration(cache, key, ttl, beta=1.0):
entry = cache.get(key)
if entry is None:
return recompute_and_cache(cache, key, ttl)
value, expiry, delta = entry
# delta = time it took to recompute last time
# Probabilistically refresh before actual expiry
if time.time() - delta * beta * random.random() >= expiry:
return recompute_and_cache(cache, key, ttl)
return value
```
## CDN Caching
Content Delivery Networks cache responses at edge locations close to users.
### Cache-Control Headers
```http
Cache-Control: public, max-age=3600, s-maxage=86400, stale-while-revalidate=60
```
| Directive | Meaning |
|-----------|---------|
| `public` | Any cache (CDN, proxy, browser) may store the response |
| `private` | Only the browser may cache (not CDN/proxies) |
| `max-age=N` | Browser cache TTL in seconds |
| `s-maxage=N` | CDN/proxy cache TTL (overrides max-age for shared caches) |
| `no-cache` | Must revalidate with origin before using cached copy |
| `no-store` | Do not cache at all (sensitive data) |
| `stale-while-revalidate=N` | Serve stale for N seconds while revalidating in background |
| `stale-if-error=N` | Serve stale for N seconds if origin returns an error |
| `immutable` | Content will never change (versioned assets) |
### Edge Caching Strategy
- Cache static assets with long TTLs and content-hash filenames (`app.a1b2c3.js` with `immutable`).
- Cache API responses at the CDN with `s-maxage` and `stale-while-revalidate`.
- Use cache tags or surrogate keys for targeted invalidation (supported by Fastly, CloudFront, Cloudflare).
## Application Caching
### Redis vs. Memcached
| Feature | Redis | Memcached |
|---------|-------|-----------|
| **Data structures** | Strings, hashes, lists, sets, sorted sets, streams | Strings only |
| **Persistence** | RDB snapshots, AOF log | None |
| **Replication** | Built-in primary/replica | None (client-side sharding) |
| **Pub/Sub** | Yes | No |
| **Lua scripting** | Yes | No |
| **Max value size** | 512 MB | 1 MB |
| **Multi-threading** | Single-threaded (I/O threads in 6.0+) | Multi-threaded |
**Recommendation:** Use Redis unless you need only simple string caching and prefer Memcached's multi-threaded model for pure throughput at scale.
### In-Memory / Local Cache
- Fastest possible access (no network hop).
- Limited by process memory; not shared across instances.
- Use for extremely hot data with short TTLs (e.g., config, feature flags, rate limit counters).
- Examples: Caffeine (Java), MemoryCache (.NET), node-cache (Node.js), lru-cache (Python).
## HTTP Caching
### ETag and Last-Modified
```
Response:
ETag: "abc123"
Last-Modified: Tue, 15 Jan 2024 10:00:00 GMT
Subsequent request:
If-None-Match: "abc123"
If-Modified-Since: Tue, 15 Jan 2024 10:00:00 GMT
Server response if unchanged:
304 Not Modified (no body, saves bandwidth)
```
- **ETag** -- opaque identifier for a specific version of a resource (hash of content or version number).
- **Last-Modified** -- timestamp of last change.
- Both enable conditional requests that save bandwidth when content has not changed.
## Multi-Tier Caching
```
┌─────────────┐ ┌─────────────────┐ ┌──────────┐ ┌──────────┐
│ Client │───>│ CDN (L3) │───>│ Redis │───>│ Database │
│ Browser │ │ Edge cache │ │ (L2) │ │ │
│ cache (L0) │ │ │ │ Distrib. │ │ │
└─────────────┘ └─────────────────┘ │ cache │ │ │
└──────────┘ └──────────┘
▲
┌──────────┐
│ In-proc │
│ cache(L1)│
└──────────┘
```
| Tier | Location | Latency | Shared | Capacity |
|------|----------|---------|--------|----------|
| **L0 — Browser** | Client device | ~0 ms | No | Small |
| **L1 — In-process** | Application memory | ~0.01 ms | No (per-instance) | Small-Medium |
| **L2 — Distributed** | Redis / Memcached | ~1 ms | Yes (all instances) | Large |
| **L3 — CDN** | Edge PoP | ~10-50 ms | Yes (per-region) | Very large |
| **Origin** | Database | ~5-50 ms | Yes | Unlimited |
**Strategy:** Check L1 first, then L2, then L3, then origin. Write-through from origin to L2; let L1 fill on demand with short TTLs. CDN serves public, cacheable content.
## Best Practices
- Always set a TTL on every cache entry -- even if you also use event-based invalidation. TTL is your safety net against stale data from missed events.
- Monitor cache hit rates. A hit rate below 80% suggests the cache is not well-tuned for actual access patterns. Investigate and adjust.
- Design for cache failure gracefully: the system must function (possibly with degraded performance) when the cache is unavailable.
- Never cache sensitive data (credentials, tokens, PII) without encryption and strict TTLs.
- Use consistent hashing for distributed cache clusters to minimize key redistribution when nodes are added or removed.
- Prefer cache-aside as the starting pattern. Only move to more complex patterns (write-through, write-behind) when you have measured evidence that they are needed.
- Implement cache stampede protection (locking or probabilistic refresh) for any high-traffic cache keys with expensive recomputation.
backend/caching/README.md
# Caching Strategies & Patterns
Use when designing caching strategies — choosing between cache-aside, read-through, write-through, write-behind, and write-around patterns, planning cache invalidation, and implementing multi-tier caching architectures.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 7 individual best practice rules |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/backend/caching
```
## License
MIT
backend/README.md
# Backend Architecture
Use when making backend architecture decisions — choosing API styles, database types, caching strategies, authentication mechanisms, and server-side design patterns for scalable, maintainable systems.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 6 individual best practice rules |
## Sub-skills
| Skill | Description |
|-------|-------------|
| [`api-design/`](api-design/) | Use when designing APIs — REST endpoints, GraphQL schemas, gRPC services, or WebSocket protocols — including resource na... |
| [`authentication/`](authentication/) | Use when designing authentication and authorization systems — OAuth 2.0 flows, JWT handling, session management, RBAC/AB... |
| [`caching/`](caching/) | Use when designing caching strategies — choosing between cache-aside, read-through, write-through, write-behind, and wri... |
| [`data-modeling/`](data-modeling/) | Use when designing database schemas, choosing data modeling strategies, or making decisions about data storage architect... |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev/backend
```
## License
MIT
backend/SKILL.md
---
name: backend
description: |
Use when making backend architecture decisions — choosing API styles, database types, caching strategies, authentication mechanisms, and server-side design patterns for scalable, maintainable systems.
USE FOR: backend architecture decisions, choosing API styles, choosing database types, server-side design patterns, backend system design
DO NOT USE FOR: specific pattern details (use sub-skills: data-modeling, api-design, caching, authentication), frontend architecture (use dev/frontend), infrastructure (use iac)
license: MIT
metadata:
displayName: "Backend Architecture"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Martin Fowler — Patterns of Enterprise Application Architecture"
url: "https://martinfowler.com/eaaCatalog/"
- title: "OpenAPI Specification"
url: "https://www.openapis.org/"
---
# Backend Architecture
## Overview
Backend architecture encompasses the server-side decisions that determine how a system stores data, exposes functionality, handles security, and scales under load. The choices made at this level -- API style, database type, caching strategy, authentication mechanism -- ripple through every layer of the application and are difficult to change once established.
This skill provides a decision-making framework drawn from Martin Kleppmann's *Designing Data-Intensive Applications* and industry-proven patterns for building reliable, scalable, and maintainable backend systems.
## Knowledge Map
```
┌─────────────────────────────────────────────────────────────────┐
│ API Layer │
│ REST, GraphQL, gRPC, WebSocket │
│ → How clients communicate with the backend │
├─────────────────────────────────────────────────────────────────┤
│ Data Storage │ Caching │
│ Relational, Document, Graph, │ In-memory, Distributed, │
│ Key-Value, Time-Series │ CDN, HTTP caching │
│ → How data is persisted │ → How hot data is served │
├─────────────────────────────────────────────────────────────────┤
│ Authentication & Authorization │ Background Processing │
│ OAuth 2.0, JWT, RBAC, ABAC, │ Job queues, schedulers, │
│ Multi-tenancy │ event-driven workers │
├─────────────────────────────────────────────────────────────────┤
│ Rate Limiting & Throttling │ Observability │
│ Token bucket, sliding window, │ Logging, metrics, tracing, │
│ API quotas, circuit breakers │ health checks, alerting │
└─────────────────────────────────────────────────────────────────┘
```
## Choosing an API Style
| Criterion | REST | GraphQL | gRPC | WebSocket |
|-----------|------|---------|------|-----------|
| **Best for** | CRUD resources, public APIs | Flexible queries, mobile clients | Internal microservices, high throughput | Real-time bidirectional communication |
| **Data format** | JSON (typically) | JSON | Protobuf (binary) | Any (JSON, binary) |
| **Contract** | OpenAPI / Swagger | Schema (SDL) | .proto files | No standard schema |
| **Caching** | HTTP caching (excellent) | Harder (POST-based) | No HTTP caching | Not cacheable |
| **Streaming** | SSE (server-only) | Subscriptions | Bidirectional streaming | Full-duplex native |
| **Browser support** | Native | Native | Requires gRPC-Web proxy | Native |
| **Learning curve** | Low | Medium | Medium-High | Low-Medium |
| **Over/under-fetching** | Common problem | Solved by design | Defined per RPC | N/A |
| **Tooling maturity** | Excellent | Good | Good (growing) | Moderate |
**Decision heuristic:**
- Default to **REST** for public APIs and simple CRUD services.
- Choose **GraphQL** when clients need flexible, aggregated queries across multiple resources (especially mobile).
- Choose **gRPC** for internal service-to-service communication where latency and throughput matter.
- Choose **WebSocket** when you need real-time, bidirectional data flow (chat, live dashboards, collaborative editing).
- Many systems combine styles: REST for public API, gRPC internally, WebSocket for real-time features.
## Choosing a Database Type
| Criterion | Relational (SQL) | Document (NoSQL) | Graph | Key-Value | Time-Series |
|-----------|------------------|-------------------|-------|-----------|-------------|
| **Best for** | Structured data, complex joins, ACID transactions | Flexible schemas, nested data, rapid iteration | Highly connected data, relationship traversal | Simple lookups, caching, session storage | Metrics, IoT, logs, financial ticks |
| **Examples** | PostgreSQL, MySQL, SQL Server | MongoDB, CouchDB, DynamoDB | Neo4j, Amazon Neptune, ArangoDB | Redis, Memcached, DynamoDB | InfluxDB, TimescaleDB, Prometheus |
| **Schema** | Strict (schema-on-write) | Flexible (schema-on-read) | Property graph / RDF | Schema-free | Tag + field model |
| **Scaling** | Vertical (horizontal with sharding) | Horizontal (built-in) | Vertical (some horizontal) | Horizontal (built-in) | Horizontal (built-in) |
| **Transactions** | Full ACID | Limited (document-level) | Varies by product | None (typically) | None (typically) |
| **Query language** | SQL | Vendor-specific (MQL, etc.) | Cypher, Gremlin, SPARQL | GET/SET commands | InfluxQL, Flux, SQL |
| **Joins** | Excellent | Poor (application-level) | Excellent (traversals) | None | Limited |
**Decision heuristic:**
- Default to **relational** (PostgreSQL) when data is structured and relationships matter.
- Choose **document** when schema flexibility and developer velocity are priorities, and joins are rare.
- Choose **graph** when the primary queries traverse relationships (social networks, recommendations, fraud detection).
- Choose **key-value** for caching, sessions, and simple lookup-by-key workloads.
- Choose **time-series** for append-heavy, time-stamped data with downsampling and retention needs.
- Polyglot persistence is common: use the right database for each bounded context.
## Backend Architecture Concerns
### Rate Limiting & Throttling
Protect backend services from abuse and overload:
- **Token Bucket** -- allows bursts up to a configured capacity, refills at a steady rate.
- **Sliding Window** -- counts requests in a rolling time window for smoother limiting.
- **Fixed Window** -- simple counter per time window (risk of burst at window boundaries).
- **Leaky Bucket** -- processes requests at a constant rate, queuing excess.
- Implement at the API gateway layer for consistency across services.
### Background Processing
Offload long-running or non-urgent work from the request/response cycle:
- **Job queues** (Sidekiq, Celery, Hangfire, BullMQ) -- enqueue work, process asynchronously.
- **Scheduled jobs** (cron, Quartz, Hangfire recurring) -- time-triggered processing.
- **Event-driven workers** -- react to domain events from a message broker.
- **Batch processing** -- periodic bulk operations (ETL, report generation).
- Always design for idempotency -- workers may process the same job more than once.
### Observability
The three pillars of observability, plus health monitoring:
- **Logging** -- structured logs (JSON) with correlation IDs for request tracing.
- **Metrics** -- counters, gauges, histograms (request rate, error rate, latency percentiles).
- **Distributed Tracing** -- end-to-end trace across services (OpenTelemetry, Jaeger, Zipkin).
- **Health checks** -- liveness (is the process running?) and readiness (can it serve traffic?).
- **Alerting** -- thresholds on key metrics (error rate > 1%, p99 latency > 500ms).
## Canonical Reference
- *Designing Data-Intensive Applications* by Martin Kleppmann -- the definitive guide to data storage, replication, partitioning, encoding, and distributed system trade-offs. Essential reading for any backend architect.
## Sub-Skills
- `dev/backend/data-modeling` -- Data modeling and database architecture patterns
- `dev/backend/api-design` -- API design patterns for REST, GraphQL, gRPC, WebSocket
- `dev/backend/caching` -- Caching strategies and patterns
- `dev/backend/authentication` -- Authentication and authorization patterns
## Best Practices
- Start with a monolith and extract services only when complexity demands it -- premature microservices add coordination cost without proportional benefit.
- Choose boring technology by default. PostgreSQL, Redis, and a well-designed REST API solve the vast majority of backend problems.
- Design for failure: every network call can fail, every database can be slow. Use timeouts, retries with backoff, circuit breakers, and fallbacks.
- Make operations idempotent wherever possible -- especially for writes, background jobs, and event handlers.
- Instrument everything from day one. Adding observability retroactively is far more expensive than building it in.
- Treat API contracts as public commitments: version explicitly, deprecate gracefully, never break existing clients without a migration path.
README.md
# Development Fundamentals
Use when working with fundamental software development knowledge — patterns, algorithms, architecture, and craftsmanship principles drawn from canonical published works.
## Structure
| File | Purpose |
|------|---------|
| `SKILL.md` | Agent skill definition (frontmatter + instructions) |
| `metadata.json` | Machine-readable metadata and versioning |
| `AGENTS.md` | Agent-optimized quick reference (generated) |
| `README.md` | This file |
| `rules/` | 7 individual best practice rules |
## Sub-skills
| Skill | Description |
|-------|-------------|
| [`algorithms/`](algorithms/) | Use when selecting algorithms, analyzing complexity, or reasoning about data structure choices. Covers Big-O notation, s... |
| [`architecture/`](architecture/) | Use when selecting architecture styles, evaluating system decomposition strategies, or analyzing architecture characteri... |
| [`backend/`](backend/) | Use when making backend architecture decisions — choosing API styles, database types, caching strategies, authentication... |
| [`craftsmanship/`](craftsmanship/) | Use when applying software craftsmanship principles — code quality, professional practices, and continuous improvement d... |
| [`design-patterns/`](design-patterns/) | Gang of Four (GoF) design patterns — 23 proven object-oriented solutions organized into Creational, Structural, and Beha... |
| [`frontend/`](frontend/) | Frontend architecture approaches — from Multi-Page Apps through Single Page Apps, Server-Side Rendering, Islands Archite... |
| [`integration-patterns/`](integration-patterns/) | Use when designing or evaluating enterprise integration architectures based on Hohpe & Woolf's Enterprise Integration Pa... |
## Usage
```bash
npx agentskills add Tyler-R-Kendrick/agent-skills/skills/dev
```
## License
MIT
SKILL.md
---
name: dev
description: |
Use when working with fundamental software development knowledge — patterns, algorithms, architecture, and craftsmanship principles drawn from canonical published works.
USE FOR: development fundamentals, pattern selection, architecture decisions, algorithm choice, code quality principles, choosing between architectural styles
DO NOT USE FOR: specific pattern implementations (use sub-skills: design-patterns, integration-patterns, algorithms, etc.), testing strategy (use testing), infrastructure (use iac)
license: MIT
metadata:
displayName: "Development Fundamentals"
author: "Tyler-R-Kendrick"
compatibility: claude, copilot, cursor
references:
- title: "Refactoring.Guru — Design Patterns"
url: "https://refactoring.guru/design-patterns"
- title: "Enterprise Integration Patterns — Hohpe & Woolf"
url: "https://www.enterpriseintegrationpatterns.com/"
- title: "Martin Fowler — Software Architecture Guide"
url: "https://martinfowler.com/architecture/"
---
# Development Fundamentals
## Overview
This skill covers the foundational knowledge every software developer should command — drawn from canonical published works and industry-proven practices. It spans from low-level algorithms through code craftsmanship to system-level architecture.
## Knowledge Map
```
┌─────────────────────────────────────────────────────────┐
│ Architecture │
│ Microservices, Monoliths, DDD, Event-Driven, │
│ Hexagonal, Well-Architected Frameworks │
├─────────────────────────────────────────────────────────┤
│ Frontend │ Backend │
│ SPA, PWA, Micro-frontends, │ Data Modeling, API │
│ SSR, Islands Architecture │ Design, Caching, Auth │
├─────────────────────────────────────────────────────────┤
│ Integration Patterns │ Design Patterns │
│ EIP: Messaging, Routing, │ GoF: Creational, │
│ Transformation, Endpoints │ Structural, Behavioral │
├─────────────────────────────────────────────────────────┤
│ Algorithms & Data Structures │
│ Sorting, Searching, Graphs, DP, Combinatorial │
├─────────────────────────────────────────────────────────┤
│ Craftsmanship │
│ Clean Code, Clean Architecture, SOLID, 12-Factor, │
│ Refactoring, Boy Scout Rule │
└─────────────────────────────────────────────────────────┘
```
## Canonical Works
| Book | Author | Covers |
|------|--------|--------|
| *Design Patterns* | Gamma, Helm, Johnson, Vlissides (GoF) | 23 object-oriented patterns |
| *Enterprise Integration Patterns* | Hohpe & Woolf | Messaging, routing, transformation |
| *The Art of Computer Programming* | Donald Knuth | Algorithms, data structures, combinatorics |
| *Clean Code* | Robert C. Martin | Naming, functions, formatting, comments |
| *Clean Architecture* | Robert C. Martin | Dependency rule, boundaries, layers |
| *Refactoring* | Martin Fowler | Code smells, refactoring catalog |
| *Domain-Driven Design* | Eric Evans | Bounded contexts, aggregates, ubiquitous language |
| *Building Microservices* | Sam Newman | Service decomposition, communication, deployment |
| *The Pragmatic Programmer* | Hunt & Thomas | Career, approach, tools, pragmatic philosophy |
| *The Twelve-Factor App* | Adam Wiggins (Heroku) | Cloud-native application methodology |
| *Release It!* | Michael Nygard | Stability patterns, capacity, deployment |
| *Fundamentals of Software Architecture* | Richards & Ford | Architecture styles, characteristics, decisions |
## Choosing the Right Pattern Category
| Problem | Look In |
|---------|---------|
| Object creation complexity | Design Patterns → Creational |
| Composing objects / adapting interfaces | Design Patterns → Structural |
| Object communication / state management | Design Patterns → Behavioral |
| Service-to-service messaging | Integration Patterns |
| Algorithm selection / optimization | Algorithms |
| Code readability and maintainability | Craftsmanship |
| System decomposition and boundaries | Architecture |
| Client-side application structure | Frontend |
| Server-side data and API structure | Backend |
## Best Practices
- Learn patterns as a vocabulary, not a checklist — apply them when the problem calls for it, not preemptively.
- Start with the simplest architecture that works (monolith), evolve toward complexity (microservices) only when you have evidence you need it.
- Apply the Boy Scout Rule: leave code better than you found it, every time you touch it.
- Use SOLID principles as guardrails for daily decisions, not just for greenfield design.
- Prefer composition over inheritance — most GoF patterns are variations of this principle.
- Study algorithms for problem-solving intuition, not memorization — know when to reach for a graph algorithm vs. dynamic programming.
- Keep integration patterns in mind whenever systems need to communicate — messaging solves problems that synchronous calls create.