mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-12 19:25:55 +00:00
141 lines
5.4 KiB
Markdown
141 lines
5.4 KiB
Markdown
# Finding Defensive Patterns (Step 5)
|
||
|
||
Defensive code patterns are evidence of past failures or known risks. Every null guard, try/catch, normalization function, and sentinel check exists because something went wrong — or because someone anticipated it would. Your job is to find these patterns systematically and convert them into fitness-to-purpose scenarios and boundary tests.
|
||
|
||
## Systematic Search
|
||
|
||
Don't skim — grep the codebase methodically. The exact patterns depend on the project's language. Here are common defensive-code indicators grouped by what they protect against:
|
||
|
||
**Null/nil guards:**
|
||
|
||
| Language | Grep pattern |
|
||
|---|---|
|
||
| Python | `None`, `is None`, `is not None` |
|
||
| Java | `null`, `Optional`, `Objects.requireNonNull` |
|
||
| Scala | `Option`, `None`, `.getOrElse`, `.isEmpty` |
|
||
| TypeScript | `undefined`, `null`, `??`, `?.` |
|
||
| Go | `== nil`, `!= nil`, `if err != nil` |
|
||
| Rust | `Option`, `unwrap`, `.is_none()`, `?` |
|
||
|
||
**Exception/error handling:**
|
||
|
||
| Language | Grep pattern |
|
||
|---|---|
|
||
| Python | `except`, `try:`, `raise` |
|
||
| Java | `catch`, `throws`, `try {` |
|
||
| Scala | `Try`, `catch`, `recover`, `Failure` |
|
||
| TypeScript | `catch`, `throw`, `.catch(` |
|
||
| Go | `if err != nil`, `errors.New`, `fmt.Errorf` |
|
||
| Rust | `Result`, `Err(`, `unwrap_or`, `match` |
|
||
|
||
**Internal/private helpers (often defensive):**
|
||
|
||
| Language | Grep pattern |
|
||
|---|---|
|
||
| Python | `def _`, `__` |
|
||
| Java/Scala | `private`, `protected` |
|
||
| TypeScript | `private`, `#` (private fields) |
|
||
| Go | lowercase function names (unexported) |
|
||
| Rust | `pub(crate)`, non-`pub` functions |
|
||
|
||
**Sentinel values, fallbacks, boundary checks:** Search for `== 0`, `< 0`, `default`, `fallback`, `else`, `match`, `switch` — these are language-agnostic.
|
||
|
||
## What to Look For Beyond Grep
|
||
|
||
- **Bugs that were fixed** — Git history, TODO comments, workarounds, defensive code that checks for things that "shouldn't happen"
|
||
- **Design decisions** — Comments explaining "why" not just "what." Configuration that could have been hardcoded but isn't. Abstractions that exist for a reason.
|
||
- **External data quirks** — Any place the code normalizes, validates, or rejects input from an external system
|
||
- **Parsing functions** — Every parser (regex, string splitting, format detection) has failure modes. What happens with malformed input? Empty input? Unexpected types?
|
||
- **Boundary conditions** — Zero values, empty strings, maximum ranges, first/last elements, type boundaries
|
||
|
||
## Converting Findings to Scenarios
|
||
|
||
For each defensive pattern, ask: "What failure does this prevent? What input would trigger this code path?"
|
||
|
||
The answer becomes a fitness-to-purpose scenario:
|
||
|
||
```markdown
|
||
### Scenario N: [Memorable Name]
|
||
|
||
**Requirement tag:** [Req: inferred — from function_name() behavior] *(use the canonical `[Req: tier — source]` format from SKILL.md Phase 1, Step 1)*
|
||
|
||
**What happened:** [The failure mode this code prevents. Reference the actual function, file, and line. Frame as a vulnerability analysis, not a fabricated incident.]
|
||
|
||
**The requirement:** [What the code must do to prevent this failure.]
|
||
|
||
**How to verify:** [A concrete test that would fail if this regressed.]
|
||
```
|
||
|
||
## Converting Findings to Boundary Tests
|
||
|
||
Each defensive pattern also maps to a boundary test:
|
||
|
||
```python
|
||
# Python (pytest)
|
||
def test_defensive_pattern_name(fixture):
|
||
"""[Req: inferred — from function_name() guard] guards against X."""
|
||
# Mutate fixture to trigger the defensive code path
|
||
# Assert the system handles it gracefully
|
||
```
|
||
|
||
```java
|
||
// Java (JUnit 5)
|
||
@Test
|
||
@DisplayName("[Req: inferred — from methodName() guard] guards against X")
|
||
void testDefensivePatternName() {
|
||
fixture.setField(null); // Trigger defensive code path
|
||
var result = process(fixture);
|
||
assertNotNull(result); // Assert graceful handling
|
||
}
|
||
```
|
||
|
||
```scala
|
||
// Scala (ScalaTest)
|
||
// [Req: inferred — from methodName() guard]
|
||
"defensive pattern: methodName()" should "guard against X" in {
|
||
val input = fixture.copy(field = None) // Trigger defensive code path
|
||
val result = process(input)
|
||
result should equal (defined) // Assert graceful handling
|
||
}
|
||
```
|
||
|
||
```typescript
|
||
// TypeScript (Jest)
|
||
test('[Req: inferred — from functionName() guard] guards against X', () => {
|
||
const input = { ...fixture, field: null }; // Trigger defensive code path
|
||
const result = process(input);
|
||
expect(result).toBeDefined(); // Assert graceful handling
|
||
});
|
||
```
|
||
|
||
```go
|
||
// Go (testing)
|
||
func TestDefensivePatternName(t *testing.T) {
|
||
// [Req: inferred — from FunctionName() guard] guards against X
|
||
t.Helper()
|
||
fixture.Field = nil // Trigger defensive code path
|
||
result, err := Process(fixture)
|
||
if err != nil {
|
||
t.Fatalf("expected graceful handling, got error: %v", err)
|
||
}
|
||
// Assert the system handled it
|
||
}
|
||
```
|
||
|
||
```rust
|
||
// Rust (cargo test)
|
||
#[test]
|
||
fn test_defensive_pattern_name() {
|
||
// [Req: inferred — from function_name() guard] guards against X
|
||
let input = Fixture { field: None, ..default_fixture() };
|
||
let result = process(&input);
|
||
assert!(result.is_ok(), "expected graceful handling");
|
||
}
|
||
```
|
||
|
||
## Minimum Bar
|
||
|
||
You should find at least 2–3 defensive patterns per source file in the core logic modules. If you find fewer, read function bodies more carefully — not just signatures and comments.
|
||
|
||
For a medium-sized project (5–15 source files), expect to find 15–30 defensive patterns total. Each one should produce at least one boundary test.
|