Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit 4e62c55

Browse files
committed
docs: add reference checklists for testing, security, performance
- testing-patterns.md: AAA pattern, mock boundaries, Rust test patterns, anti-patterns, prove-it workflow - security-checklist.md: OWASP Top 10, secret scanning, cargo audit, SQL safety, input validation - performance-checklist.md: cargo bench/flamegraph, DB anti-patterns, async/await patterns, tokio runtime Task: fn-136-borrow-agent-skills-patterns-anti.2
1 parent 1ddf9f3 commit 4e62c55

3 files changed

Lines changed: 460 additions & 0 deletions

File tree

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# Performance Checklist
2+
3+
Quick reference for performance in flow-code. Rust backend with libSQL, async runtime.
4+
5+
## Measurement Commands
6+
7+
```bash
8+
# Rust benchmarks (add benches/ directory with criterion)
9+
cargo bench
10+
11+
# Criterion (statistical benchmarking)
12+
cargo bench --bench my_benchmark
13+
14+
# Flamegraph (CPU profiling)
15+
cargo install flamegraph
16+
cargo flamegraph --bin flowctl -- status --json
17+
18+
# Memory profiling (Linux)
19+
valgrind --tool=massif target/release/flowctl status --json
20+
21+
# Binary size
22+
ls -lh target/release/flowctl
23+
cargo bloat --release --crates
24+
25+
# Compile time
26+
cargo build --release --timings
27+
```
28+
29+
| Tool | What It Measures | When to Use |
30+
|---|---|---|
31+
| `cargo bench` (criterion) | Function-level latency | Before/after optimization |
32+
| `cargo flamegraph` | CPU hotspots | Investigating slow commands |
33+
| `cargo bloat` | Binary size by crate | When binary grows unexpectedly |
34+
| `valgrind --tool=massif` | Heap allocations | Investigating memory usage |
35+
| `hyperfine` | CLI command latency | Comparing command performance |
36+
37+
## Database Anti-Patterns (libSQL)
38+
39+
| Anti-Pattern | Impact | Fix |
40+
|---|---|---|
41+
| N+1 queries | Linear DB round-trips | Use JOINs or batch queries |
42+
| Unbounded queries | Memory exhaustion, timeouts | Always use LIMIT, paginate |
43+
| Missing indexes | Slow reads as data grows | Add indexes on filtered/sorted columns |
44+
| No connection pooling | Connection overhead per query | Reuse connections |
45+
| SELECT * | Fetches unused columns | Select only needed columns |
46+
| String concatenation in SQL | Injection risk + no query cache | Use parameterized queries |
47+
| No transactions for batch ops | Partial failures, inconsistency | Wrap in BEGIN/COMMIT |
48+
49+
## Backend CLI Checklist
50+
51+
| Area | Target | How to Verify |
52+
|---|---|---|
53+
| Command startup | < 50ms | `hyperfine 'flowctl status --json'` |
54+
| Task list (100 tasks) | < 200ms | Benchmark with test data |
55+
| Database open | < 20ms | Profile with `tracing` spans |
56+
| JSON serialization | < 5ms for typical output | `cargo bench` |
57+
| Binary size | < 20MB release | `ls -lh target/release/flowctl` |
58+
| Memory usage (idle) | < 10MB | `valgrind` or `/usr/bin/time -v` |
59+
60+
## Rust-Specific Performance
61+
62+
### Async/Await Patterns
63+
64+
| Do | Don't |
65+
|---|---|
66+
| Use `tokio::spawn` for concurrent I/O | Block the async runtime with sync I/O |
67+
| Use `tokio::task::spawn_blocking` for CPU work | Run CPU-heavy code in async context |
68+
| Use `tokio::join!` for parallel awaits | Await sequentially when independent |
69+
| Use bounded channels for backpressure | Use unbounded channels (memory leak risk) |
70+
71+
```rust
72+
// Good: parallel async operations
73+
let (tasks, epics) = tokio::join!(
74+
db.list_tasks(),
75+
db.list_epics()
76+
);
77+
78+
// Bad: sequential when independent
79+
let tasks = db.list_tasks().await?;
80+
let epics = db.list_epics().await?; // Waits unnecessarily
81+
```
82+
83+
### Memory Patterns
84+
85+
| Do | Don't |
86+
|---|---|
87+
| Use `&str` over `String` where possible | Clone strings unnecessarily |
88+
| Use `Cow<str>` for maybe-owned data | Allocate when borrowing suffices |
89+
| Pre-allocate with `Vec::with_capacity` | Push to vec without size hint |
90+
| Use iterators over collecting into vecs | `collect()` when you can chain |
91+
| Use `Arc` for shared read-only data | Clone large structs across threads |
92+
93+
```rust
94+
// Good: pre-allocate
95+
let mut results = Vec::with_capacity(tasks.len());
96+
97+
// Bad: repeated reallocation
98+
let mut results = Vec::new(); // Grows 1, 2, 4, 8...
99+
```
100+
101+
### String Performance
102+
103+
| Do | Don't |
104+
|---|---|
105+
| Use `write!` or `format!` once | Repeated `push_str` / `+` concatenation |
106+
| Use `String::with_capacity` for building | Build strings without size hint |
107+
| Return `impl Display` for lazy formatting | Format eagerly when output may not be used |
108+
| Use `serde_json::to_writer` for streaming | Serialize to String then write |
109+
110+
## Tokio Runtime
111+
112+
| Setting | Recommendation |
113+
|---|---|
114+
| Runtime flavor | `current_thread` for CLI (lower overhead) |
115+
| Worker threads | Default for server, 1 for CLI tools |
116+
| Blocking threads | Default (512), reduce for CLI |
117+
| Task budget | Let tokio manage; avoid manual yielding |
118+
119+
```rust
120+
// CLI tool: single-threaded runtime (faster startup)
121+
#[tokio::main(flavor = "current_thread")]
122+
async fn main() { ... }
123+
124+
// Server: multi-threaded (default)
125+
#[tokio::main]
126+
async fn main() { ... }
127+
```
128+
129+
## Performance Anti-Patterns
130+
131+
| Anti-Pattern | Impact | Fix |
132+
|---|---|---|
133+
| Blocking async runtime | Starves other tasks | Use `spawn_blocking` |
134+
| N+1 database queries | Linear latency growth | Batch or JOIN queries |
135+
| Cloning large structs | Memory + CPU waste | Use references or `Arc` |
136+
| Unbounded collections | Memory exhaustion | Add limits, use streaming |
137+
| Sync file I/O in async | Blocks executor thread | Use `tokio::fs` |
138+
| Excessive logging in hot path | I/O overhead | Use log levels, sample |
139+
| Compiling in debug mode for perf tests | 10-100x slower | Always `--release` |
140+
| Not reusing DB connections | Connection overhead | Use connection pool |
141+
| Serializing then parsing JSON | Double work | Pass typed structs |
142+
| Formatting strings nobody reads | CPU waste | Lazy formatting with `Display` |
143+
144+
## Profiling Workflow
145+
146+
| Step | Command | Purpose |
147+
|---|---|---|
148+
| 1. Baseline | `hyperfine 'flowctl status --json'` | Measure current perf |
149+
| 2. Profile | `cargo flamegraph --bin flowctl -- status` | Find hotspots |
150+
| 3. Optimize | Edit code targeting hotspot | Reduce cost |
151+
| 4. Verify | Re-run baseline command | Confirm improvement |
152+
| 5. Benchmark | `cargo bench` | Prevent regression |
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Security Checklist
2+
3+
Quick reference for security in flow-code. CLI tool handling local state, no web server.
4+
5+
## Pre-Commit Secret Scan
6+
7+
```bash
8+
# Check staged files for secrets before committing
9+
git diff --cached | grep -iE \
10+
"password\s*=|secret\s*=|api_key\s*=|token\s*=|private_key|BEGIN RSA|BEGIN EC"
11+
12+
# Patterns to catch
13+
grep -rn "AKIA[0-9A-Z]{16}" # AWS access keys
14+
grep -rn "sk-[a-zA-Z0-9]{48}" # OpenAI API keys
15+
grep -rn "ghp_[a-zA-Z0-9]{36}" # GitHub personal tokens
16+
grep -rn "xoxb-[0-9]{10,}" # Slack bot tokens
17+
```
18+
19+
## Files to Never Commit
20+
21+
| File/Pattern | Reason | Mitigation |
22+
|---|---|---|
23+
| `.env` | Contains secrets | Add to `.gitignore` |
24+
| `*.pem`, `*.key` | Private keys | Add to `.gitignore` |
25+
| `.flow/` | Runtime state (may contain tokens) | Add to `.gitignore` |
26+
| `credentials.json` | Service account keys | Add to `.gitignore` |
27+
| `*.upstream` | Backup files with potential secrets | Add to `.gitignore` |
28+
29+
Commit `.env.example` with placeholder values instead of `.env`.
30+
31+
## Secrets Management
32+
33+
| Do | Don't |
34+
|---|---|
35+
| Use environment variables for secrets | Hardcode secrets in source |
36+
| Commit `.env.example` with placeholders | Commit `.env` with real values |
37+
| Use `--json` flag for machine parsing | Parse human-readable output with secrets |
38+
| Rotate secrets after exposure | Ignore leaked credentials |
39+
| Scope API keys to minimum permissions | Use admin keys for all operations |
40+
41+
## OWASP Top 10 Quick Reference
42+
43+
| # | Vulnerability | Prevention |
44+
|---|---|---|
45+
| 1 | Broken Access Control | Auth checks on every endpoint, ownership verification |
46+
| 2 | Cryptographic Failures | HTTPS, strong hashing, no secrets in code |
47+
| 3 | Injection | Parameterized queries, input validation |
48+
| 4 | Insecure Design | Threat modeling, spec-driven development |
49+
| 5 | Security Misconfiguration | Minimal permissions, audit deps, secure defaults |
50+
| 6 | Vulnerable Components | `cargo audit`, `cargo deny`, minimal deps |
51+
| 7 | Auth Failures | Strong passwords, rate limiting, session management |
52+
| 8 | Data Integrity Failures | Verify updates/dependencies, signed artifacts |
53+
| 9 | Logging Failures | Log security events, don't log secrets |
54+
| 10 | SSRF | Validate/allowlist URLs, restrict outbound requests |
55+
56+
## Dependency Audit (Rust)
57+
58+
```bash
59+
# Audit for known vulnerabilities
60+
cargo audit
61+
62+
# Deny specific licenses or advisories
63+
cargo deny check
64+
65+
# Check for outdated dependencies
66+
cargo outdated
67+
68+
# Minimal dependency tree (fewer deps = smaller attack surface)
69+
cargo tree | wc -l
70+
```
71+
72+
| Tool | Purpose | When to Run |
73+
|---|---|---|
74+
| `cargo audit` | CVE database check | Pre-commit, CI |
75+
| `cargo deny` | License + advisory policy | CI pipeline |
76+
| `cargo outdated` | Stale dependency check | Weekly/monthly |
77+
| `cargo tree` | Dependency graph audit | When adding deps |
78+
79+
## Input Validation
80+
81+
| Rule | Example |
82+
|---|---|
83+
| Validate at boundaries only | CLI args, JSON input, file reads |
84+
| Use allowlists, not denylists | Accept known-good, reject everything else |
85+
| Constrain string lengths | Task titles: 1-500 chars |
86+
| Validate file paths | No `..` traversal, stay within `.flow/` |
87+
| Sanitize output for shells | Escape special chars in generated commands |
88+
| Parse, don't validate | Deserialize into typed structs, not raw strings |
89+
90+
```rust
91+
// Good: parse into typed struct at boundary
92+
let task: TaskInput = serde_json::from_str(&input)?;
93+
94+
// Bad: pass raw string around and validate later
95+
fn process(raw: &str) { /* who validates this? */ }
96+
```
97+
98+
## Error Handling
99+
100+
| Context | Approach |
101+
|---|---|
102+
| CLI output (user-facing) | Clear error message, no stack traces |
103+
| JSON output (`--json`) | Structured error: `{"error": "message"}` |
104+
| Internal logging | Full context, file/line, but no secrets |
105+
| Panics | Catch at boundaries with `catch_unwind` or `?` |
106+
107+
```rust
108+
// Good: generic error to user, detail in logs
109+
eprintln!("Error: failed to open database");
110+
tracing::error!("DB open failed: {:?} at {:?}", err, path);
111+
112+
// Bad: expose internals
113+
eprintln!("Error: {}", err); // May leak path, SQL, etc.
114+
```
115+
116+
## SQL Safety (libSQL)
117+
118+
| Do | Don't |
119+
|---|---|
120+
| Use parameterized queries | Concatenate user input into SQL |
121+
| Use `?` placeholders | Use `format!()` for query building |
122+
| Validate types before query | Trust raw input as safe |
123+
| Use transactions for multi-step ops | Execute multiple queries without atomicity |
124+
125+
```rust
126+
// Good: parameterized
127+
conn.execute("INSERT INTO tasks (title) VALUES (?1)", [&title]).await?;
128+
129+
// Bad: string concatenation
130+
conn.execute(&format!("INSERT INTO tasks (title) VALUES ('{}')", title), ()).await?;
131+
```
132+
133+
## File System Security
134+
135+
| Rule | Rationale |
136+
|---|---|
137+
| Validate paths stay within `.flow/` | Prevent directory traversal |
138+
| Use temp dirs for scratch work | Don't pollute user directories |
139+
| Set restrictive permissions on state files | Protect `.flow/` data |
140+
| Clean up temp files on exit | No stale sensitive data |
141+
| Don't follow symlinks blindly | Potential symlink attacks |

0 commit comments

Comments
 (0)