@deftai/directive-content 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/deft/SKILL.md +6 -0
- package/.agents/skills/deft-directive-article-review/SKILL.md +11 -0
- package/.agents/skills/deft-directive-build/SKILL.md +10 -0
- package/.agents/skills/deft-directive-cost/SKILL.md +12 -0
- package/.agents/skills/deft-directive-debug/SKILL.md +13 -0
- package/.agents/skills/deft-directive-gh-arch/SKILL.md +11 -0
- package/.agents/skills/deft-directive-gh-slice/SKILL.md +10 -0
- package/.agents/skills/deft-directive-glossary/SKILL.md +10 -0
- package/.agents/skills/deft-directive-interview/SKILL.md +9 -0
- package/.agents/skills/deft-directive-pre-pr/SKILL.md +9 -0
- package/.agents/skills/deft-directive-refinement/SKILL.md +10 -0
- package/.agents/skills/deft-directive-release/SKILL.md +12 -0
- package/.agents/skills/deft-directive-review-cycle/SKILL.md +10 -0
- package/.agents/skills/deft-directive-setup/SKILL.md +10 -0
- package/.agents/skills/deft-directive-swarm/SKILL.md +10 -0
- package/.agents/skills/deft-directive-sync/SKILL.md +9 -0
- package/.agents/skills/deft-directive-triage/SKILL.md +9 -0
- package/.agents/skills/deft-directive-write-skill/SKILL.md +9 -0
- package/LICENSE.md +22 -0
- package/QUICK-START.md +167 -0
- package/UPGRADING.md +517 -0
- package/coding/build-output.md +28 -0
- package/coding/coding.md +235 -0
- package/coding/debugging.md +110 -0
- package/coding/holzmann.md +96 -0
- package/coding/hygiene.md +127 -0
- package/coding/security.md +158 -0
- package/coding/testing.md +162 -0
- package/coding/toolchain.md +44 -0
- package/commands.md +300 -0
- package/context/context.md +57 -0
- package/context/deterministic-split.md +67 -0
- package/context/examples.md +26 -0
- package/context/fractal-summaries.md +69 -0
- package/context/long-horizon.md +46 -0
- package/context/spec-deltas.md +177 -0
- package/context/tool-design.md +34 -0
- package/context/working-memory.md +62 -0
- package/contracts/boundary-maps.md +65 -0
- package/contracts/deterministic-questions.md +59 -0
- package/contracts/hierarchy.md +77 -0
- package/conventions/content-manifest.json +399 -0
- package/conventions/machine-generated-banner.md +130 -0
- package/conventions/references.md +120 -0
- package/conventions/rule-ownership.json +382 -0
- package/conventions/task-caching.md +43 -0
- package/conventions/vbrief-filenames.md +70 -0
- package/deployments/README.md +37 -0
- package/deployments/agentuity/README.md +138 -0
- package/deployments/agentuity/via-cli.md +380 -0
- package/deployments/agentuity/via-cloud.md +425 -0
- package/deployments/agentuity/via-github-actions.md +664 -0
- package/deployments/agentuity/via-gravity-network.md +606 -0
- package/deployments/agentuity/via-vpc.md +607 -0
- package/deployments/aws/README.md +38 -0
- package/deployments/aws/via-app-runner.md +612 -0
- package/deployments/aws/via-ecs-fargate.md +561 -0
- package/deployments/aws/via-elastic-beanstalk.md +628 -0
- package/deployments/aws/via-lambda.md +649 -0
- package/deployments/azure/README.md +37 -0
- package/deployments/azure/via-aks.md +390 -0
- package/deployments/azure/via-app-service.md +564 -0
- package/deployments/azure/via-container-apps.md +599 -0
- package/deployments/azure/via-functions.md +552 -0
- package/deployments/cloud-gov/README.md +63 -0
- package/deployments/cloud-gov/agents/compliance-docs.md +154 -0
- package/deployments/cloud-gov/agents.md +39 -0
- package/deployments/cloud-gov/cicd.md +64 -0
- package/deployments/cloud-gov/deployment.md +150 -0
- package/deployments/cloud-gov/logging.md +43 -0
- package/deployments/cloud-gov/manifest.md +121 -0
- package/deployments/cloud-gov/overview.md +58 -0
- package/deployments/cloud-gov/security.md +46 -0
- package/deployments/cloud-gov/services.md +72 -0
- package/deployments/cloud-gov/upstream/README.md +18 -0
- package/deployments/cloudflare/README.md +33 -0
- package/deployments/cloudflare/via-dashboard.md +83 -0
- package/deployments/cloudflare/via-git.md +90 -0
- package/deployments/cloudflare/via-github-actions.md +185 -0
- package/deployments/cloudflare/via-terraform.md +157 -0
- package/deployments/cloudflare/via-wrangler.md +165 -0
- package/deployments/fly-io/README.md +37 -0
- package/deployments/fly-io/via-dockerfile.md +648 -0
- package/deployments/fly-io/via-flyctl.md +653 -0
- package/deployments/fly-io/via-github-actions.md +695 -0
- package/deployments/fly-io/via-multi-region.md +598 -0
- package/deployments/google/README.md +34 -0
- package/deployments/google/via-app-engine.md +42 -0
- package/deployments/google/via-cloud-functions.md +23 -0
- package/deployments/google/via-cloud-run.md +330 -0
- package/deployments/google/via-gke.md +23 -0
- package/deployments/netlify/README.md +99 -0
- package/deployments/netlify/via-cli.md +17 -0
- package/deployments/netlify/via-functions.md +19 -0
- package/deployments/netlify/via-git.md +25 -0
- package/deployments/vercel/README.md +90 -0
- package/deployments/vercel/via-api.md +16 -0
- package/deployments/vercel/via-cli.md +17 -0
- package/deployments/vercel/via-git.md +24 -0
- package/docs/BROWNFIELD.md +179 -0
- package/docs/getting-started.md +137 -0
- package/docs/good-agents-md.md +137 -0
- package/events/README.md +89 -0
- package/events/event-record.schema.json +26 -0
- package/events/registry.json +166 -0
- package/events/registry.schema.json +71 -0
- package/glossary.md +145 -0
- package/incidents/README.md +81 -0
- package/incidents/_template.md +38 -0
- package/interfaces/cli.md +104 -0
- package/interfaces/rest.md +212 -0
- package/interfaces/tui.md +242 -0
- package/interfaces/web.md +123 -0
- package/languages/6502-DASM.md +132 -0
- package/languages/c.md +235 -0
- package/languages/commands.md +23 -0
- package/languages/cpp.md +132 -0
- package/languages/csharp.md +259 -0
- package/languages/dart.md +183 -0
- package/languages/delphi.md +218 -0
- package/languages/elixir.md +208 -0
- package/languages/go.md +78 -0
- package/languages/java.md +278 -0
- package/languages/javascript.md +163 -0
- package/languages/julia.md +175 -0
- package/languages/kotlin.md +193 -0
- package/languages/markdown.md +168 -0
- package/languages/mermaid.md +146 -0
- package/languages/officejs.md +392 -0
- package/languages/python.md +209 -0
- package/languages/r.md +163 -0
- package/languages/rust.md +216 -0
- package/languages/sql.md +216 -0
- package/languages/swift.md +153 -0
- package/languages/typescript.md +132 -0
- package/languages/vba.md +279 -0
- package/languages/vhdl.md +180 -0
- package/languages/visual-basic.md +151 -0
- package/languages/zig.md +196 -0
- package/meta/SOUL.md +27 -0
- package/meta/code-field.md +44 -0
- package/meta/morals.md +40 -0
- package/meta/philosophy.md +39 -0
- package/meta/project.md +49 -0
- package/meta/ralph.md +223 -0
- package/meta/security.md +80 -0
- package/meta/versioning.md +326 -0
- package/package.json +22 -0
- package/packs/lessons/lessons-pack-0.1.json +553 -0
- package/packs/patterns/patterns-pack-0.1.json +57 -0
- package/packs/rules/rules-pack-0.1.json +4767 -0
- package/packs/skills/skills-pack-0.1.json +262 -0
- package/packs/strategies/strategies-pack-0.1.json +167 -0
- package/packs/swarm-spec/swarm-spec-pack-0.1.json +17 -0
- package/patterns/executor-layer-credentials.md +227 -0
- package/patterns/llm-app.md +156 -0
- package/patterns/multi-agent.md +278 -0
- package/patterns/prompt-assembly-layer-ordering.md +154 -0
- package/patterns/role-as-overlay.md +179 -0
- package/platforms/2600.md +137 -0
- package/platforms/unity.md +329 -0
- package/references/composer-skill-porting.md +152 -0
- package/references/cost-models.md +163 -0
- package/references/ip-risk.md +246 -0
- package/references/plain-english-ux.md +275 -0
- package/resilience/context-pruning.md +67 -0
- package/resilience/continue-here.md +62 -0
- package/scm/changelog.md +276 -0
- package/scm/git.md +139 -0
- package/scm/github.md +265 -0
- package/secrets/.gitkeep +0 -0
- package/skills/deft-build/SKILL.md +20 -0
- package/skills/deft-directive-article-review/SKILL.md +156 -0
- package/skills/deft-directive-build/SKILL.md +302 -0
- package/skills/deft-directive-cost/SKILL.md +201 -0
- package/skills/deft-directive-debug/SKILL.md +140 -0
- package/skills/deft-directive-decompose/SKILL.md +96 -0
- package/skills/deft-directive-gh-arch/SKILL.md +160 -0
- package/skills/deft-directive-gh-slice/SKILL.md +199 -0
- package/skills/deft-directive-glossary/SKILL.md +118 -0
- package/skills/deft-directive-interview/SKILL.md +528 -0
- package/skills/deft-directive-pre-pr/SKILL.md +131 -0
- package/skills/deft-directive-probe/SKILL.md +127 -0
- package/skills/deft-directive-refinement/SKILL.md +403 -0
- package/skills/deft-directive-release/SKILL.md +266 -0
- package/skills/deft-directive-review-cycle/SKILL.md +401 -0
- package/skills/deft-directive-setup/SKILL.md +717 -0
- package/skills/deft-directive-swarm/SKILL.md +989 -0
- package/skills/deft-directive-sync/SKILL.md +288 -0
- package/skills/deft-directive-triage/SKILL.md +137 -0
- package/skills/deft-directive-write-skill/SKILL.md +169 -0
- package/skills/deft-interview/SKILL.md +16 -0
- package/skills/deft-pre-pr/SKILL.md +16 -0
- package/skills/deft-review-cycle/SKILL.md +16 -0
- package/skills/deft-roadmap-refresh/SKILL.md +16 -0
- package/skills/deft-setup/SKILL.md +20 -0
- package/skills/deft-swarm/SKILL.md +16 -0
- package/skills/deft-sync/SKILL.md +16 -0
- package/strategies/README.md +83 -0
- package/strategies/artifact-guards.md +85 -0
- package/strategies/bdd.md +115 -0
- package/strategies/brownfield.md +7 -0
- package/strategies/discuss.md +129 -0
- package/strategies/emit-hints.md +69 -0
- package/strategies/enterprise.md +193 -0
- package/strategies/interview.md +551 -0
- package/strategies/map.md +179 -0
- package/strategies/probe.md +151 -0
- package/strategies/rapid.md +155 -0
- package/strategies/research.md +155 -0
- package/strategies/roadmap.md +9 -0
- package/strategies/speckit.md +437 -0
- package/strategies/v0-20-contract.md +134 -0
- package/strategies/yolo.md +169 -0
- package/swarm/swarm.md +300 -0
- package/templates/COST-ESTIMATE.md +114 -0
- package/templates/PULL_REQUEST_TEMPLATE.md +35 -0
- package/templates/agent-prompt-preamble.md +409 -0
- package/templates/agents-entry.md +211 -0
- package/templates/agents-entry.placeholders.md +75 -0
- package/templates/embed.go +20 -0
- package/templates/embed_test.go +36 -0
- package/templates/make-spec-example.md +9 -0
- package/templates/make-spec.md +246 -0
- package/templates/project.md.template +52 -0
- package/templates/specification.md +1 -0
- package/templates/swarm-greptile-poller-prompt.md +556 -0
- package/templates/user.md.template +31 -0
- package/tools/RWLDL.md +80 -0
- package/tools/greptile.md +141 -0
- package/tools/installer.md +23 -0
- package/tools/taskfile-migration.md +32 -0
- package/tools/taskfile.md +185 -0
- package/tools/telemetry.md +285 -0
- package/vbrief/schemas/cache-meta.schema.json +137 -0
- package/vbrief/schemas/candidates.schema.json +130 -0
- package/vbrief/schemas/codebase-map.schema.json +213 -0
- package/vbrief/schemas/lessons-pack.schema.json +134 -0
- package/vbrief/schemas/patterns-pack.schema.json +84 -0
- package/vbrief/schemas/rules-pack.schema.json +105 -0
- package/vbrief/schemas/skills-pack.schema.json +94 -0
- package/vbrief/schemas/slices.schema.json +87 -0
- package/vbrief/schemas/strategies-pack.schema.json +89 -0
- package/vbrief/schemas/swarm-spec-pack.schema.json +84 -0
- package/vbrief/schemas/vbrief-core.schema.json +1022 -0
- package/vbrief/vbrief.md +684 -0
- package/verification/integration.md +76 -0
- package/verification/plan-checking.md +85 -0
- package/verification/uat.md +60 -0
- package/verification/verification.md +117 -0
package/languages/r.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# R Standards
|
|
2
|
+
|
|
3
|
+
Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
|
|
4
|
+
|
|
5
|
+
**⚠️ See also**: [main.md](../../main.md) | [PROJECT.md](../../PROJECT.md)
|
|
6
|
+
|
|
7
|
+
**Stack**: R 4.3+, tidyverse; Packages: devtools/usethis; Testing: testthat 3; Docs: roxygen2; Lint: lintr; Style: styler
|
|
8
|
+
|
|
9
|
+
## Standards
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
- ! roxygen2 comments (`#'`) for all exported functions, classes, and datasets
|
|
13
|
+
- ! Document `@param`, `@return`, `@examples`, `@export` for public functions
|
|
14
|
+
- ! Include a `README.Rmd` or `README.md` for packages
|
|
15
|
+
- ~ Use `@seealso` and `@family` to cross-reference related functions
|
|
16
|
+
|
|
17
|
+
### Testing
|
|
18
|
+
See [testing.md](../coding/testing.md).
|
|
19
|
+
|
|
20
|
+
- ! Use testthat 3 (`test_that()`, `expect_*()`)
|
|
21
|
+
- ! Place tests in `tests/testthat/`; files: `test-*.R`
|
|
22
|
+
- ~ Use snapshot tests (`expect_snapshot()`) for complex output
|
|
23
|
+
|
|
24
|
+
### Coverage
|
|
25
|
+
- ! ≥85% coverage (measured via covr)
|
|
26
|
+
- ! Count R/\*
|
|
27
|
+
- ! Exclude entry points, generated files, vignettes
|
|
28
|
+
|
|
29
|
+
### Style
|
|
30
|
+
- ! Follow [Tidyverse Style Guide](https://style.tidyverse.org/)
|
|
31
|
+
- ! Use lintr for static analysis (project `.lintr` config checked in)
|
|
32
|
+
- ! Use styler for auto-formatting
|
|
33
|
+
- ! 2-space indentation, no tabs
|
|
34
|
+
- ! Line length ≤80 characters
|
|
35
|
+
|
|
36
|
+
### Naming Conventions
|
|
37
|
+
- ! `snake_case` for functions, variables, and file names: `read_data()`, `user_count`
|
|
38
|
+
- ! `PascalCase` for R6/S4 class names: `DataProcessor`, `ModelConfig`
|
|
39
|
+
- ! `UPPER_SNAKE_CASE` for true constants
|
|
40
|
+
- ! Prefix internal/non-exported functions with `.`: `.validate_input()`
|
|
41
|
+
- ⊗ Dots in function names for new code (`read.csv` style): use `snake_case`
|
|
42
|
+
- ⊗ Single-letter variable names except in math/formula contexts (`x`, `y`, `n`)
|
|
43
|
+
|
|
44
|
+
### Types & Data
|
|
45
|
+
- ! Use tibbles (`tibble`/`data.frame`) over lists for tabular data
|
|
46
|
+
- ! Use explicit types: `integer(1)`, `character(0)`, `logical(1)` for scalars
|
|
47
|
+
- ~ Use `rlang::check_required()` / `rlang::abort()` for argument validation
|
|
48
|
+
- ~ Use named lists or S3/R6 classes for structured non-tabular data
|
|
49
|
+
- ⊗ Use `T`/`F` — always spell out `TRUE`/`FALSE`
|
|
50
|
+
- ⊗ Rely on partial argument matching
|
|
51
|
+
|
|
52
|
+
### Functions
|
|
53
|
+
- ! Pure functions preferred: same inputs → same outputs, no side effects
|
|
54
|
+
- ! One function per purpose; keep under 50 lines where practical
|
|
55
|
+
- ! Use `...` (dots) deliberately; document what they pass to
|
|
56
|
+
- ⊗ Modify global state from inside functions (`<<-`, `assign()` to `.GlobalEnv`)
|
|
57
|
+
- ~ Use `invisible()` for functions called for side effects that return invisibly
|
|
58
|
+
- ~ Use `tryCatch()` / `withCallingHandlers()` for error handling
|
|
59
|
+
|
|
60
|
+
### Dependencies
|
|
61
|
+
- ! Minimize dependencies; use `Imports` not `Depends` in DESCRIPTION
|
|
62
|
+
- ! Pin major versions in `renv.lock` for reproducibility
|
|
63
|
+
- ! Use `renv` for project-level dependency isolation
|
|
64
|
+
- ⊗ Use `library()` inside package code — use `::` or `@importFrom`
|
|
65
|
+
- ~ Use `Suggests` for test/vignette-only dependencies
|
|
66
|
+
|
|
67
|
+
### Vectorization & Performance
|
|
68
|
+
- ! Prefer vectorized operations over explicit loops
|
|
69
|
+
- ~ Use `purrr::map()` family for functional iteration
|
|
70
|
+
- ~ Use `data.table` or `dplyr` for data manipulation (not base `for` loops on data frames)
|
|
71
|
+
- ≉ Use `apply()` family when `purrr` or vectorized alternatives exist
|
|
72
|
+
- ≉ Grow vectors in a loop (`c(vec, new_elem)`) — pre-allocate instead
|
|
73
|
+
|
|
74
|
+
### Pipelines
|
|
75
|
+
- ! Use native pipe `|>` (R 4.1+) or `%>%` from magrittr
|
|
76
|
+
- ! Keep pipe chains readable (one operation per line)
|
|
77
|
+
- ≉ Pipe chains longer than 10 steps — extract into named intermediate variables or functions
|
|
78
|
+
- ~ Use `.data` and `.env` pronouns in tidy evaluation for clarity
|
|
79
|
+
|
|
80
|
+
### Reproducibility
|
|
81
|
+
- ! Use `renv` for project dependency snapshots
|
|
82
|
+
- ! Set `set.seed()` before any stochastic operation; document seed in reports
|
|
83
|
+
- ! Use relative paths; ⊗ absolute paths or `setwd()`
|
|
84
|
+
- ~ Use `here::here()` for project-relative paths
|
|
85
|
+
- ~ Use Quarto or R Markdown for reproducible reports
|
|
86
|
+
|
|
87
|
+
### Security
|
|
88
|
+
- ⊗ Hardcode secrets or credentials in source
|
|
89
|
+
- ! Validate/sanitize all external inputs (file paths, user data, API responses)
|
|
90
|
+
- ~ Use `.Renviron` for secrets; load via `Sys.getenv()`
|
|
91
|
+
|
|
92
|
+
### Telemetry
|
|
93
|
+
- ~ Structured logging (logger, futile.logger) for production
|
|
94
|
+
- ? OpenTelemetry (via REST/Python bridge) for distributed tracing
|
|
95
|
+
|
|
96
|
+
## Commands
|
|
97
|
+
|
|
98
|
+
See [commands.md](./commands.md).
|
|
99
|
+
|
|
100
|
+
## Patterns
|
|
101
|
+
|
|
102
|
+
### Testing (testthat 3)
|
|
103
|
+
```r
|
|
104
|
+
test_that("calculate_mean returns correct mean", {
|
|
105
|
+
expect_equal(calculate_mean(c(1, 2, 3)), 2)
|
|
106
|
+
expect_equal(calculate_mean(c(10, 20)), 15)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test_that("calculate_mean errors on non-numeric input", {
|
|
110
|
+
expect_error(calculate_mean("abc"), "must be numeric")
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test_that("calculate_mean handles NA with na.rm", {
|
|
114
|
+
expect_equal(calculate_mean(c(1, NA, 3), na.rm = TRUE), 2)
|
|
115
|
+
expect_equal(calculate_mean(c(1, NA, 3), na.rm = FALSE), NA_real_)
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### roxygen2 Documentation
|
|
120
|
+
```r
|
|
121
|
+
#' @param x Numeric vector. @param weights Numeric vector (same length). @param na.rm Remove NAs?
|
|
122
|
+
#' @return Single numeric. @export
|
|
123
|
+
weighted_mean <- function(x, weights, na.rm = FALSE) {
|
|
124
|
+
stopifnot(is.numeric(x), is.numeric(weights), length(x) == length(weights))
|
|
125
|
+
if (na.rm) { keep <- !is.na(x) & !is.na(weights); x <- x[keep]; weights <- weights[keep] }
|
|
126
|
+
sum(x * weights) / sum(weights)
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Tidy Pipeline
|
|
131
|
+
```r
|
|
132
|
+
summary_by_group <- raw_data |>
|
|
133
|
+
filter(!is.na(value)) |> group_by(category) |>
|
|
134
|
+
summarise(mean_val = mean(value), n = n(), .groups = "drop") |>
|
|
135
|
+
arrange(desc(mean_val))
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Error Handling (rlang)
|
|
139
|
+
```r
|
|
140
|
+
read_config <- function(path) {
|
|
141
|
+
if (!file.exists(path)) rlang::abort(paste0("Config not found: ", path), class = "config_not_found_error")
|
|
142
|
+
tryCatch(yaml::read_yaml(path), error = function(e) rlang::abort("Failed to parse config", parent = e))
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Anti-Patterns
|
|
147
|
+
|
|
148
|
+
Items marked ⊗ in Standards above are not repeated here.
|
|
149
|
+
|
|
150
|
+
- ≉ **Dot-separated names** (`my.function`): Use `snake_case`
|
|
151
|
+
- ≉ **`sapply()`**: Use `vapply()` or `purrr::map_*()`
|
|
152
|
+
- ≉ **Pipe chains >10 steps**: Extract named intermediates
|
|
153
|
+
|
|
154
|
+
## Compliance Checklist
|
|
155
|
+
|
|
156
|
+
- ! roxygen2 docs on all exported functions
|
|
157
|
+
- ! See [testing.md](../coding/testing.md) for testing requirements
|
|
158
|
+
- ! testthat 3 for unit tests; ≥85% coverage via covr
|
|
159
|
+
- ! lintr + styler configured and enforced
|
|
160
|
+
- ! Tidyverse style; `snake_case` naming; `TRUE`/`FALSE` spelled out
|
|
161
|
+
- ! `renv` for dependency reproducibility
|
|
162
|
+
- ⊗ `T`/`F`, `<<-`, `setwd()`, `library()` in packages, absolute paths
|
|
163
|
+
- ! Run `task check` before commit
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Rust Standards
|
|
2
|
+
|
|
3
|
+
Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
|
|
4
|
+
|
|
5
|
+
**⚠️ See also**: [main.md](../../main.md) | [PROJECT.md](../../PROJECT.md)
|
|
6
|
+
|
|
7
|
+
**Stack**: Rust (latest stable); Build: Cargo; Testing: built-in `#[test]` + proptest; Lint: clippy; Format: rustfmt; Docs: rustdoc
|
|
8
|
+
|
|
9
|
+
## Standards
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
- ! rustdoc comments (`///`) on all public items (functions, structs, enums, traits, modules)
|
|
13
|
+
- ! Include `# Examples` section with runnable doc-tests for public API
|
|
14
|
+
- ! Module-level docs (`//!`) explaining purpose and usage
|
|
15
|
+
- ~ Use `# Errors`, `# Panics`, `# Safety` sections where applicable
|
|
16
|
+
- ⊗ `#[allow(missing_docs)]` on public items without justification
|
|
17
|
+
|
|
18
|
+
### Testing
|
|
19
|
+
See [testing.md](../coding/testing.md).
|
|
20
|
+
|
|
21
|
+
- ! Unit tests in `#[cfg(test)] mod tests` at bottom of each module
|
|
22
|
+
- ! Integration tests in `tests/` directory for public API
|
|
23
|
+
- ! Use `#[should_panic]` or `assert!(result.is_err())` for error cases
|
|
24
|
+
- ~ Use proptest or quickcheck for property-based testing
|
|
25
|
+
- ~ Use doc-tests as living examples (they run during `cargo test`)
|
|
26
|
+
|
|
27
|
+
### Coverage
|
|
28
|
+
- ! ≥80% coverage (measured via cargo-tarpaulin or llvm-cov)
|
|
29
|
+
- ! Count src/**
|
|
30
|
+
- ! Exclude main.rs entry points, generated code, build scripts
|
|
31
|
+
|
|
32
|
+
### Style
|
|
33
|
+
- ! Run rustfmt on all code (project `rustfmt.toml` checked in)
|
|
34
|
+
- ! Run clippy with `#![deny(clippy::all)]` at crate level
|
|
35
|
+
- ! `#![deny(clippy::pedantic)]` for library crates
|
|
36
|
+
- ! 4-space indentation (rustfmt default)
|
|
37
|
+
- ! Line length ≤100 characters
|
|
38
|
+
- ~ Use `#![warn(clippy::nursery)]` for additional catches
|
|
39
|
+
|
|
40
|
+
### Naming Conventions
|
|
41
|
+
- ! `snake_case` for functions, methods, variables, modules, crate names
|
|
42
|
+
- ! `PascalCase` for types (structs, enums, traits), type parameters
|
|
43
|
+
- ! `SCREAMING_SNAKE_CASE` for constants and statics
|
|
44
|
+
- ! Prefix lifetime parameters with `'`: `'a`, `'input`
|
|
45
|
+
- ! Trait names: prefer adjective/verb forms: `Display`, `Iterator`, `Send`
|
|
46
|
+
- ⊗ Hungarian notation or type prefixes
|
|
47
|
+
- ~ Getter methods: use field name directly (`fn name(&self)` not `fn get_name`)
|
|
48
|
+
|
|
49
|
+
### Ownership & Borrowing
|
|
50
|
+
- ! Prefer borrowing (`&T`, `&mut T`) over cloning
|
|
51
|
+
- ! Use `Clone` only when ownership transfer is truly needed
|
|
52
|
+
- ! Prefer `&str` over `String` in function parameters
|
|
53
|
+
- ! Prefer `&[T]` over `Vec<T>` in function parameters
|
|
54
|
+
- ⊗ `.unwrap()` or `.expect()` in library code — return `Result` or `Option`
|
|
55
|
+
- ~ Use `Cow<'_, str>` when a function may or may not need to allocate
|
|
56
|
+
- ≉ Excessive `.clone()` to appease the borrow checker — redesign ownership
|
|
57
|
+
|
|
58
|
+
### Error Handling
|
|
59
|
+
- ! Use `Result<T, E>` for recoverable errors; define custom error types
|
|
60
|
+
- ! Use `thiserror` for library error types; `anyhow` for application error types
|
|
61
|
+
- ! Implement `std::error::Error` for all custom error types
|
|
62
|
+
- ! Use `?` operator for error propagation
|
|
63
|
+
- ⊗ `.unwrap()` in production code (except after infallible checks with comments)
|
|
64
|
+
- ⊗ `panic!()` for expected error conditions
|
|
65
|
+
- ~ Use `#[must_use]` on functions returning `Result` or important values
|
|
66
|
+
|
|
67
|
+
### Concurrency
|
|
68
|
+
- ! Use `Send` + `Sync` bounds correctly; understand their implications
|
|
69
|
+
- ! Prefer channels (`mpsc`, `crossbeam`) over shared mutable state
|
|
70
|
+
- ! Use `Arc<Mutex<T>>` or `Arc<RwLock<T>>` when shared state is necessary
|
|
71
|
+
- ⊗ `unsafe` to circumvent Send/Sync bounds
|
|
72
|
+
- ~ Use `tokio` or `async-std` for async I/O; avoid mixing runtimes
|
|
73
|
+
- ~ Prefer `RwLock` over `Mutex` for read-heavy workloads
|
|
74
|
+
|
|
75
|
+
### Unsafe Code
|
|
76
|
+
- ! Minimize `unsafe` blocks; isolate behind safe abstractions
|
|
77
|
+
- ! Document `# Safety` section explaining invariants for every `unsafe fn`
|
|
78
|
+
- ! Document `// SAFETY:` comment on every `unsafe` block explaining why it's sound
|
|
79
|
+
- ⊗ `unsafe` without clear justification and safety proof
|
|
80
|
+
- ~ Use `#![forbid(unsafe_code)]` for crates that don't need it
|
|
81
|
+
|
|
82
|
+
### Dependencies
|
|
83
|
+
- ! Pin dependencies in `Cargo.lock` (always commit for binaries)
|
|
84
|
+
- ! Audit dependencies with `cargo-audit`
|
|
85
|
+
- ! Minimize dependency count; prefer `std` library functionality
|
|
86
|
+
- ~ Use `cargo-deny` to enforce license and duplicate dependency policies
|
|
87
|
+
- ≉ `*` version specifiers in `Cargo.toml`
|
|
88
|
+
|
|
89
|
+
### Performance
|
|
90
|
+
- ! Use iterators and zero-cost abstractions over manual loops where idiomatic
|
|
91
|
+
- ! Prefer stack allocation; use `Box<T>` only when heap is necessary
|
|
92
|
+
- ~ Use `#[inline]` sparingly and only with benchmarks to justify
|
|
93
|
+
- ~ Profile with `criterion` for benchmarks; `perf` or `flamegraph` for profiling
|
|
94
|
+
- ≉ Premature optimization over readability
|
|
95
|
+
|
|
96
|
+
### Macros
|
|
97
|
+
- ~ Prefer functions and generics over macros when possible
|
|
98
|
+
- ! Document all `macro_rules!` with examples
|
|
99
|
+
- ! Keep macro complexity minimal; extract logic into functions
|
|
100
|
+
- ⊗ Macros that generate non-obvious control flow
|
|
101
|
+
|
|
102
|
+
### Security
|
|
103
|
+
- ⊗ Hardcode secrets or credentials in source
|
|
104
|
+
- ! Validate all external inputs (file paths, network data, user input)
|
|
105
|
+
- ! Use `secrecy` crate for sensitive values (keys, tokens)
|
|
106
|
+
- ~ Use `zeroize` for clearing secrets from memory
|
|
107
|
+
|
|
108
|
+
### Telemetry
|
|
109
|
+
- ~ Use `tracing` crate for structured, async-aware logging
|
|
110
|
+
- ~ Use `tracing-opentelemetry` for distributed tracing
|
|
111
|
+
- ? Use `metrics` crate for Prometheus-compatible metrics
|
|
112
|
+
|
|
113
|
+
## Commands
|
|
114
|
+
|
|
115
|
+
See [commands.md](./commands.md).
|
|
116
|
+
|
|
117
|
+
## Patterns
|
|
118
|
+
|
|
119
|
+
### Error Handling (thiserror)
|
|
120
|
+
```rust
|
|
121
|
+
#[derive(Debug, thiserror::Error)]
|
|
122
|
+
pub enum AppError {
|
|
123
|
+
#[error("config not found: {path}")] ConfigNotFound { path: String },
|
|
124
|
+
#[error("invalid config: {0}")] InvalidConfig(String),
|
|
125
|
+
#[error(transparent)] Io(#[from] std::io::Error),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
pub fn load_config(path: &str) -> Result<Config, AppError> {
|
|
129
|
+
if !std::path::Path::new(path).exists() {
|
|
130
|
+
return Err(AppError::ConfigNotFound { path: path.to_string() });
|
|
131
|
+
}
|
|
132
|
+
let content = std::fs::read_to_string(path)?;
|
|
133
|
+
parse_config(&content).map_err(AppError::InvalidConfig)
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Testing (Table-Driven)
|
|
138
|
+
```rust
|
|
139
|
+
#[cfg(test)]
|
|
140
|
+
mod tests {
|
|
141
|
+
use super::*;
|
|
142
|
+
#[test]
|
|
143
|
+
fn test_parse_amount() {
|
|
144
|
+
let cases = [("100", Ok(100)), ("0", Ok(0)), ("-5", Err("negative")), ("abc", Err("invalid"))];
|
|
145
|
+
for (input, expected) in cases {
|
|
146
|
+
match expected {
|
|
147
|
+
Ok(val) => assert_eq!(parse_amount(input).unwrap(), val, "{input}"),
|
|
148
|
+
Err(msg) => assert!(parse_amount(input).unwrap_err().to_string().contains(msg), "{input}"),
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Builder Pattern
|
|
156
|
+
```rust
|
|
157
|
+
#[derive(Debug, Default)]
|
|
158
|
+
pub struct RequestBuilder { url: String, timeout: Option<u64>, headers: Vec<(String, String)> }
|
|
159
|
+
|
|
160
|
+
impl RequestBuilder {
|
|
161
|
+
pub fn new(url: impl Into<String>) -> Self { Self { url: url.into(), ..Default::default() } }
|
|
162
|
+
pub fn timeout(mut self, secs: u64) -> Self { self.timeout = Some(secs); self }
|
|
163
|
+
pub fn header(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
|
|
164
|
+
self.headers.push((k.into(), v.into())); self
|
|
165
|
+
}
|
|
166
|
+
pub fn build(self) -> Result<Request, BuildError> { Ok(Request { /* ... */ }) }
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Structured Logging (tracing)
|
|
171
|
+
```rust
|
|
172
|
+
#[instrument(skip(db), fields(user_id = %user_id))]
|
|
173
|
+
pub async fn get_user(db: &Pool, user_id: i64) -> Result<User, AppError> {
|
|
174
|
+
info!("fetching user");
|
|
175
|
+
let user = db.query_one("SELECT ...", &[&user_id]).await?;
|
|
176
|
+
if user.is_inactive() { warn!("inactive user accessed"); }
|
|
177
|
+
Ok(user)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Anti-Patterns
|
|
182
|
+
|
|
183
|
+
Items marked ⊗ in Standards above are not repeated here.
|
|
184
|
+
|
|
185
|
+
- ≉ **`Arc<Mutex<T>>` as first resort**: Consider channels/message passing
|
|
186
|
+
- ≉ **Stringly-typed errors**: Use `thiserror`
|
|
187
|
+
- ≉ **God-struct with many `Option` fields**: Use builder pattern or state types
|
|
188
|
+
- ≉ **`Box<dyn Error>` in libraries**: Define specific error enums
|
|
189
|
+
|
|
190
|
+
## Hygiene
|
|
191
|
+
|
|
192
|
+
**Types:**
|
|
193
|
+
- ⊗ `dyn Any` or `Box<dyn Any>` where a concrete type or typed trait object is knowable
|
|
194
|
+
- ⊗ `#[allow(unused)]` or `#[allow(dead_code)]` attributes to suppress rather than remove dead code
|
|
195
|
+
|
|
196
|
+
**Error handling:**
|
|
197
|
+
- ⊗ `.unwrap()` or `.expect()` outside test code — use `?` or explicit match
|
|
198
|
+
- ⊗ `let _ = result` to silently ignore a `Result` or `Option` — handle or document why safe
|
|
199
|
+
- ⊗ Match arms `_ => {}` or `Err(_) => {}` that swallow errors without logging or propagating
|
|
200
|
+
|
|
201
|
+
**Dead code:**
|
|
202
|
+
- ~ `cargo check` warns on unused items; `cargo clippy` detects dead helpers and unreachable branches
|
|
203
|
+
- ~ Use `cargo +nightly udeps` to detect unused dependencies in `Cargo.toml`
|
|
204
|
+
- ⊗ Commented-out code — delete; version control preserves history
|
|
205
|
+
|
|
206
|
+
## Compliance Checklist
|
|
207
|
+
|
|
208
|
+
- ! rustdoc on all public items with examples
|
|
209
|
+
- ! See [testing.md](../coding/testing.md) for testing requirements
|
|
210
|
+
- ! Unit + integration tests; ≥80% coverage
|
|
211
|
+
- ! rustfmt + clippy (pedantic for libs) enforced
|
|
212
|
+
- ! Custom error types via thiserror/anyhow; no `.unwrap()` in production
|
|
213
|
+
- ! `unsafe` minimized, documented, and justified
|
|
214
|
+
- ! `Cargo.lock` committed (binaries); `cargo-audit` clean
|
|
215
|
+
- ⊗ `.unwrap()`, `panic!()`, unguarded `unsafe`, excessive `.clone()`
|
|
216
|
+
- ! Run `task check` before commit
|
package/languages/sql.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# SQL Standards
|
|
2
|
+
|
|
3
|
+
Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
|
|
4
|
+
|
|
5
|
+
**⚠️ See also**: [main.md](../../main.md) | [PROJECT.md](../../PROJECT.md)
|
|
6
|
+
|
|
7
|
+
**Stack**: ANSI SQL (dialect-agnostic defaults); Adapts to: PostgreSQL, MySQL/MariaDB, SQL Server, SQLite, Oracle; Migrations: Flyway / Liquibase / framework-native; Lint: sqlfluff
|
|
8
|
+
|
|
9
|
+
## Standards
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
- ! Comment every non-trivial query, view, function, and stored procedure
|
|
13
|
+
- ! Document purpose, parameters, and return values for stored procedures/functions
|
|
14
|
+
- ! Include a data dictionary or schema docs for every database
|
|
15
|
+
- ~ Use inline comments (`--`) for clause-level explanations in complex queries
|
|
16
|
+
|
|
17
|
+
### Testing
|
|
18
|
+
See [testing.md](../coding/testing.md).
|
|
19
|
+
|
|
20
|
+
- ! Test migrations up and down (rollback)
|
|
21
|
+
- ! Test stored procedures, functions, and triggers with known inputs/outputs
|
|
22
|
+
- ~ Use test fixtures or seed data scripts for reproducible integration tests
|
|
23
|
+
- ~ Test edge cases: NULLs, empty sets, boundary values, large datasets
|
|
24
|
+
|
|
25
|
+
### Style & Formatting
|
|
26
|
+
- ! UPPERCASE for SQL keywords: `SELECT`, `FROM`, `WHERE`, `JOIN`, `INSERT`, `UPDATE`
|
|
27
|
+
- ! Lowercase for identifiers (tables, columns, aliases): `users`, `created_at`
|
|
28
|
+
- ! One clause per line for readability (`SELECT`, `FROM`, `WHERE`, `JOIN` each on own line)
|
|
29
|
+
- ! Indent continuation lines (subqueries, `AND`/`OR`, `CASE` branches)
|
|
30
|
+
- ! Use sqlfluff (or equivalent) for automated linting
|
|
31
|
+
- ! Line length ≤120 characters
|
|
32
|
+
- ~ Align `ON` clauses vertically when multiple joins are present
|
|
33
|
+
|
|
34
|
+
### Naming Conventions
|
|
35
|
+
- ! `snake_case` for all identifiers: tables, columns, indexes, constraints
|
|
36
|
+
- ! Singular nouns for table names: `user`, `order`, `product` (not `users`, `orders`)
|
|
37
|
+
- ! Descriptive column names: `created_at`, `is_active`, `total_amount`
|
|
38
|
+
- ! Prefix boolean columns with `is_`, `has_`, or `can_`: `is_active`, `has_shipped`
|
|
39
|
+
- ! Name primary keys `id` or `{table}_id`; foreign keys `{referenced_table}_id`
|
|
40
|
+
- ! Name indexes: `ix_{table}_{column(s)}`
|
|
41
|
+
- ! Name constraints: `pk_{table}`, `fk_{table}_{ref_table}`, `uq_{table}_{column}`, `ck_{table}_{rule}`
|
|
42
|
+
- ⊗ Reserved words as identifiers (even if quoted)
|
|
43
|
+
- ⊗ Abbreviations in names unless universally understood (`id`, `url`, `ip`)
|
|
44
|
+
|
|
45
|
+
### Queries
|
|
46
|
+
- ! Use explicit column lists — ⊗ `SELECT *` in application code or views
|
|
47
|
+
- ! Use explicit `JOIN` syntax — ⊗ implicit joins (comma-separated `FROM`)
|
|
48
|
+
- ! Qualify column names with table aliases in multi-table queries
|
|
49
|
+
- ! Use short, meaningful aliases: `u` for `user`, `o` for `order`
|
|
50
|
+
- ! Use `COALESCE()` over vendor-specific null functions (`NVL`, `IFNULL`) where portable
|
|
51
|
+
- ~ Use CTEs (`WITH`) for complex queries instead of deeply nested subqueries
|
|
52
|
+
- ~ Use `EXISTS` over `IN` for correlated subqueries (better optimizer hints)
|
|
53
|
+
- ≉ `SELECT DISTINCT` to mask duplicate-producing joins — fix the join instead
|
|
54
|
+
- ≉ `ORDER BY` column position (`ORDER BY 1, 2`) — use column names
|
|
55
|
+
|
|
56
|
+
### Parameterization & Security
|
|
57
|
+
- ! Use parameterized queries / prepared statements for all dynamic values
|
|
58
|
+
- ⊗ String concatenation for query building (SQL injection vector)
|
|
59
|
+
- ⊗ Interpolating user input directly into SQL
|
|
60
|
+
- ! Validate and sanitize inputs at the application layer before query execution
|
|
61
|
+
- ! Apply least-privilege: application accounts get only required permissions
|
|
62
|
+
- ~ Use row-level security (RLS) where supported for multi-tenant data
|
|
63
|
+
|
|
64
|
+
### Schema Design
|
|
65
|
+
- ! Every table has a primary key
|
|
66
|
+
- ! Use appropriate data types: don't store dates as strings, money as floats, etc.
|
|
67
|
+
- ! Define foreign key constraints for referential integrity
|
|
68
|
+
- ! Add `NOT NULL` constraints where business logic requires a value
|
|
69
|
+
- ! Add `CHECK` constraints for domain validation (e.g., `CHECK (quantity >= 0)`)
|
|
70
|
+
- ~ Normalize to 3NF minimum; denormalize deliberately with documentation
|
|
71
|
+
- ~ Add `created_at` and `updated_at` timestamps to mutable tables
|
|
72
|
+
- ~ Use `UUID` or `BIGINT` for primary keys (prefer `UUID` for distributed systems)
|
|
73
|
+
- ≉ Store JSON blobs where relational columns would serve better
|
|
74
|
+
- ⊗ Use `FLOAT`/`DOUBLE` for monetary values — use `DECIMAL`/`NUMERIC`
|
|
75
|
+
|
|
76
|
+
### Indexing
|
|
77
|
+
- ! Index all foreign key columns
|
|
78
|
+
- ! Index columns used in `WHERE`, `JOIN`, `ORDER BY` if query frequency justifies it
|
|
79
|
+
- ! Review and document index strategy per table
|
|
80
|
+
- ~ Use composite indexes that match query patterns (leftmost prefix rule)
|
|
81
|
+
- ~ Use partial/filtered indexes for frequently queried subsets
|
|
82
|
+
- ≉ Over-index: each index adds write overhead — justify with query patterns
|
|
83
|
+
- ⊗ Index every column "just in case"
|
|
84
|
+
|
|
85
|
+
### Migrations
|
|
86
|
+
- ! Use versioned, sequential migration files (not ad-hoc DDL)
|
|
87
|
+
- ! Every migration must be reversible (up + down)
|
|
88
|
+
- ! Test migrations against a copy of production schema before deploying
|
|
89
|
+
- ! Use transactions for DDL where supported (PostgreSQL)
|
|
90
|
+
- ~ Use idempotent migrations (`IF NOT EXISTS`, `IF EXISTS`) for resilience
|
|
91
|
+
- ⊗ Modify or delete previously applied migration files
|
|
92
|
+
|
|
93
|
+
### Transactions & Concurrency
|
|
94
|
+
- ! Use explicit transactions for multi-statement operations
|
|
95
|
+
- ! Keep transactions as short as possible
|
|
96
|
+
- ! Choose appropriate isolation level for the use case
|
|
97
|
+
- ~ Use `SELECT ... FOR UPDATE` when reading rows that will be updated
|
|
98
|
+
- ⊗ Long-running transactions that hold locks across user interactions
|
|
99
|
+
- ⊗ Implicit autocommit for multi-step business operations
|
|
100
|
+
|
|
101
|
+
### Performance
|
|
102
|
+
- ! Use `EXPLAIN` / `EXPLAIN ANALYZE` to validate query plans for critical queries
|
|
103
|
+
- ! Avoid N+1 query patterns — use joins or batch queries
|
|
104
|
+
- ! Paginate large result sets (`LIMIT`/`OFFSET` or keyset pagination)
|
|
105
|
+
- ~ Prefer keyset pagination (`WHERE id > ?`) over `OFFSET` for large tables
|
|
106
|
+
- ~ Use materialized views or caching for expensive, infrequently-changing aggregations
|
|
107
|
+
- ≉ Functions on indexed columns in `WHERE` clauses (defeats index usage)
|
|
108
|
+
- ⊗ Cartesian joins (missing `ON` / `WHERE` clause)
|
|
109
|
+
- ⊗ Unbounded `SELECT` without `LIMIT` on large tables in application code
|
|
110
|
+
|
|
111
|
+
### Stored Procedures & Functions
|
|
112
|
+
- ~ Keep business logic in the application layer; use procedures for data-intensive ops
|
|
113
|
+
- ! Document parameters, return types, and side effects
|
|
114
|
+
- ! Handle errors explicitly (`RAISE` / `SIGNAL` / `THROW`)
|
|
115
|
+
- ⊗ Deeply nested stored procedure call chains (hard to debug and test)
|
|
116
|
+
|
|
117
|
+
### Telemetry
|
|
118
|
+
- ~ Log slow queries (configure slow query log threshold)
|
|
119
|
+
- ~ Monitor index usage and table bloat
|
|
120
|
+
- ? Use query tagging (comments with trace IDs) for distributed tracing
|
|
121
|
+
|
|
122
|
+
## Commands
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
task db:migrate # Run pending migrations
|
|
126
|
+
task db:rollback # Roll back last migration
|
|
127
|
+
task db:seed # Load seed/fixture data
|
|
128
|
+
task db:reset # Drop, create, migrate, seed
|
|
129
|
+
task db:lint # Lint SQL with sqlfluff (or `task sql:lint`)
|
|
130
|
+
task db:format # Format SQL with sqlfluff fix (or `task sql:fmt`)
|
|
131
|
+
task db:schema # Dump current schema
|
|
132
|
+
task quality # All quality checks
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Note**: Single-language projects ! use generic names. Multi-language projects ! use namespaced names. See [taskfile.md](../tools/taskfile.md#naming-conventions).
|
|
136
|
+
|
|
137
|
+
## Patterns
|
|
138
|
+
|
|
139
|
+
### Parameterized Query (Application Code)
|
|
140
|
+
```sql
|
|
141
|
+
-- CORRECT: parameterized
|
|
142
|
+
SELECT id, email, created_at
|
|
143
|
+
FROM user
|
|
144
|
+
WHERE email = $1
|
|
145
|
+
AND is_active = TRUE;
|
|
146
|
+
|
|
147
|
+
-- Framework equivalent (pseudocode):
|
|
148
|
+
-- db.query("SELECT id, email FROM user WHERE email = $1", [user_email])
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### CTE for Readability
|
|
152
|
+
```sql
|
|
153
|
+
WITH active_orders AS (
|
|
154
|
+
SELECT o.user_id, COUNT(*) AS order_count, SUM(o.total_amount) AS total_spent
|
|
155
|
+
FROM order AS o
|
|
156
|
+
WHERE o.status = 'active'
|
|
157
|
+
AND o.created_at >= CURRENT_DATE - INTERVAL '30 days'
|
|
158
|
+
GROUP BY o.user_id
|
|
159
|
+
)
|
|
160
|
+
SELECT u.id, u.email, ao.order_count, ao.total_spent
|
|
161
|
+
FROM user AS u
|
|
162
|
+
INNER JOIN active_orders AS ao ON ao.user_id = u.id
|
|
163
|
+
WHERE ao.total_spent > 100.00
|
|
164
|
+
ORDER BY ao.total_spent DESC;
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Migration (Flyway-style)
|
|
168
|
+
```sql
|
|
169
|
+
-- V2__add_user_preferences.sql
|
|
170
|
+
|
|
171
|
+
-- Up
|
|
172
|
+
CREATE TABLE IF NOT EXISTS user_preference (
|
|
173
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
174
|
+
user_id UUID NOT NULL REFERENCES user (id) ON DELETE CASCADE,
|
|
175
|
+
key VARCHAR(100) NOT NULL,
|
|
176
|
+
value TEXT,
|
|
177
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
178
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
179
|
+
CONSTRAINT uq_user_preference_key UNIQUE (user_id, key)
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
CREATE INDEX ix_user_preference_user_id ON user_preference (user_id);
|
|
183
|
+
|
|
184
|
+
-- Down (in separate file or rollback section)
|
|
185
|
+
-- DROP TABLE IF EXISTS user_preference;
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Keyset Pagination
|
|
189
|
+
```sql
|
|
190
|
+
-- Efficient pagination for large tables (avoids OFFSET scan)
|
|
191
|
+
SELECT id, email, created_at
|
|
192
|
+
FROM user
|
|
193
|
+
WHERE created_at < $1 -- last seen timestamp
|
|
194
|
+
AND id < $2 -- tiebreaker
|
|
195
|
+
ORDER BY created_at DESC, id DESC
|
|
196
|
+
LIMIT 25;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Anti-Patterns
|
|
200
|
+
|
|
201
|
+
Items marked ⊗ in Standards above are not repeated here.
|
|
202
|
+
|
|
203
|
+
- ≉ **`HAVING` without `GROUP BY`**: Use `WHERE` for row-level filters
|
|
204
|
+
- ≉ **Nested subqueries >2 levels deep**: Refactor with CTEs
|
|
205
|
+
- ≉ **`NOT IN` with nullable columns**: Use `NOT EXISTS` instead (NULL semantics)
|
|
206
|
+
|
|
207
|
+
## Compliance Checklist
|
|
208
|
+
|
|
209
|
+
- ! Parameterized queries for all user-facing data access
|
|
210
|
+
- ! Explicit column lists, explicit `JOIN` syntax, qualified column names
|
|
211
|
+
- ! Primary key on every table; foreign keys with constraints
|
|
212
|
+
- ! Versioned migrations; tested up and down
|
|
213
|
+
- ! `EXPLAIN ANALYZE` reviewed for high-traffic queries
|
|
214
|
+
- ! sqlfluff lint passes; keywords uppercase, identifiers lowercase `snake_case`
|
|
215
|
+
- ⊗ `SELECT *`, implicit joins, string concatenation in queries, `FLOAT` for money
|
|
216
|
+
- ! Run `task db:lint` before commit
|