p9: add Contributing section — development setup, coding standards, testing, benchmarking, docs guide
swadhinbiswas committed 3 months ago
d81e1e5
1 parent 6165b3e
commit d81e1e5
5 files changed
+499 -0
Showing 5 changed files+499-0
▼docs_site/src/content/docs/contributing/benchmarking-guide.md
+105-0
| @@ -0,0 +1,105 @@ | ||
1 | +--- | |
2 | +title: Benchmarking Guide | |
3 | +description: How to run and interpret JustAPI benchmarks. | |
4 | +--- | |
5 | + | |
6 | +## Setup | |
7 | + | |
8 | +```bash | |
9 | +# Install benchmarking tools | |
10 | +cargo install oha | |
11 | + | |
12 | +# Build the benchmark harness | |
13 | +cargo build --release -p justapi-bench | |
14 | +``` | |
15 | + | |
16 | +## Running Benchmarks | |
17 | + | |
18 | +### Standard Workloads | |
19 | + | |
20 | +```bash | |
21 | +# Build a baseline workload | |
22 | +python benchmarks/workloads_justapi.py & | |
23 | +oha -z 30s -c 100 http://localhost:8000/hello | |
24 | +oha -z 30s -c 100 http://localhost:8000/echo | |
25 | +``` | |
26 | + | |
27 | +### CRUD Benchmarks (DB-backed) | |
28 | + | |
29 | +```bash | |
30 | +# JustAPI vs FastAPI vs Robyn with SQLite | |
31 | +bash benchmarks/run_crud_bench.sh | |
32 | +``` | |
33 | + | |
34 | +### Serialization Benchmarks | |
35 | + | |
36 | +```bash | |
37 | +cargo run --release -p justapi-bench --bin justapi-bench | |
38 | +``` | |
39 | + | |
40 | +## Benchmark Protocol | |
41 | + | |
42 | +1. **Warm up**: Minimum 5 seconds before recording | |
43 | +2. **Run**: 30 seconds, 100 concurrent connections | |
44 | +3. **Repeat**: Minimum 3 runs per workload | |
45 | +4. **Record**: p50, p95, p99 latency, RPS, peak RSS | |
46 | + | |
47 | +## Key Workloads | |
48 | + | |
49 | +| Name | Route | Method | Description | | |
50 | +|---|---|---|---| | |
51 | +| hello-world | `/hello` | GET | Static JSON response | | |
52 | +| json-echo | `/echo` | POST | Nested JSON payload echoed | | |
53 | +| db-sim | `/db` | GET | Simulated DB query (5ms sleep) | | |
54 | +| native | `/native` | POST | Native fast path (724k+ RPS) | | |
55 | +| crud-read | `/items` | GET | SQLite SELECT | | |
56 | +| crud-write | `/items` | POST | SQLite INSERT | | |
57 | + | |
58 | +## Regression Gate | |
59 | + | |
60 | +A p99 regression >5% compared to the previous baseline **fails the benchmark gate**. The CI workflow (`bench.yml`) enforces this: | |
61 | + | |
62 | +``` | |
63 | +PR_RPS >= 0.95 * MAIN_RPS # Must pass | |
64 | +``` | |
65 | + | |
66 | +## Interpreting Results | |
67 | + | |
68 | +``` | |
69 | +Requests/sec: 701,234 | |
70 | +Latency: | |
71 | + p50: 0.07 ms | |
72 | + p95: 0.12 ms | |
73 | + p99: 0.19 ms | |
74 | +``` | |
75 | + | |
76 | +- **RPS**: Higher is better | |
77 | +- **p50**: Typical latency (median) | |
78 | +- **p99**: Worst-case latency (tail) | |
79 | +- **Difference between p50 and p99**: Indicates latency consistency | |
80 | + | |
81 | +## Hardware Fixture | |
82 | + | |
83 | +Record the exact hardware when benchmarking: | |
84 | + | |
85 | +``` | |
86 | +CPU: 13th Gen Intel Core i5-13600K | |
87 | +RAM: 31 GiB DDR5 | |
88 | +OS: CachyOS Linux | |
89 | +Kernel: 6.x | |
90 | +``` | |
91 | + | |
92 | +Comparisons are only valid on the same hardware. | |
93 | + | |
94 | +## Adding New Benchmarks | |
95 | + | |
96 | +1. Add your workload to `benchmarks/workloads_*.py` | |
97 | +2. Add a new workload entry in `BENCHMARKS.md` | |
98 | +3. Run 3+ iterations and record all numbers | |
99 | +4. Do **not** overwrite historical data — append | |
100 | + | |
101 | +## See Also | |
102 | + | |
103 | +- [Performance Tuning](/advanced/performance-tuning/) — Optimize your app | |
104 | +- [BENCHMARKS.md](https://github.com/swadhinbiswas/JustAPI/blob/main/BENCHMARKS.md) | |
105 | +- [Benchmark Harness Skill](/skills/benchmark-harness/SKILL.md) | |
0 | 106 | |
▼docs_site/src/content/docs/contributing/coding-standards.md
+79-0
| @@ -0,0 +1,79 @@ | ||
1 | +--- | |
2 | +title: Coding Standards | |
3 | +description: Conventions and guidelines for contributing to JustAPI. | |
4 | +--- | |
5 | + | |
6 | +## Rust-First Mandate | |
7 | + | |
8 | +If a feature **can** be implemented in Rust, it **must** be implemented in Rust. Python is reserved for: | |
9 | + | |
10 | +- Thin PyO3 re-exports (`from ._justapi import X`) | |
11 | +- Translating Rust values to Python callables | |
12 | +- User-facing helpers with no Rust equivalent | |
13 | + | |
14 | +The following **must live in Rust**: | |
15 | +- Networking, sockets, TLS, connection management | |
16 | +- HTTP/protocol parsing and response writing | |
17 | +- Routing, middleware, rate-limiting, auth | |
18 | +- Scheduling, worker/thread pools | |
19 | +- (De)serialization of framework data | |
20 | +- Hot paths: parsing, validation, buffering, compression | |
21 | + | |
22 | +## Commit Messages | |
23 | + | |
24 | +``` | |
25 | +<phase>: <what changed> | |
26 | +``` | |
27 | + | |
28 | +Examples: | |
29 | +- `p3: add per-request arena allocator` | |
30 | +- `p42: implement paged KV cache` | |
31 | + | |
32 | +One logical change per commit. | |
33 | + | |
34 | +## Branch Naming | |
35 | + | |
36 | +``` | |
37 | +phase-N/<short-description> | |
38 | +``` | |
39 | + | |
40 | +Examples: | |
41 | +- `phase-42/paged-kv-cache` | |
42 | +- `phase-51/scheduler-engine` | |
43 | + | |
44 | +## `unsafe` Code | |
45 | + | |
46 | +Every `unsafe` block must have a `// SAFETY:` comment explaining the invariant: | |
47 | + | |
48 | +```rust | |
49 | +// SAFETY: The GIL is held, so the pointer is valid. | |
50 | +let ptr = obj.as_ptr(); | |
51 | +``` | |
52 | + | |
53 | +Whenever feasible, add a test that would fail if the invariant were violated. | |
54 | + | |
55 | +## Error Handling | |
56 | + | |
57 | +| Code Location | Error Type | | |
58 | +|---|---| | |
59 | +| Application code (CLI, bench) | `anyhow::Result` | | |
60 | +| Library crates (core, py) | Typed `thiserror` | | |
61 | + | |
62 | +## Public API Changes | |
63 | + | |
64 | +All new public APIs require documentation (rustdoc or Python docstrings) before merge. | |
65 | + | |
66 | +## Pre-Submission Checklist | |
67 | + | |
68 | +- [ ] `cargo test --workspace` passes | |
69 | +- [ ] `cargo clippy --workspace --tests -- -D warnings` clean | |
70 | +- [ ] `cargo fmt --check` clean | |
71 | +- [ ] `cargo miri test -p justapi-core` clean (if core touched) | |
72 | +- [ ] Benchmarks appended to BENCHMARKS.md (no p99 regression >5%) | |
73 | +- [ ] PLAN.md updated | |
74 | + | |
75 | +## See Also | |
76 | + | |
77 | +- [Development Setup](/contributing/development-setup/) — Get started | |
78 | +- [Testing Guide](/contributing/testing-guide/) — Writing tests | |
79 | +- [Documentation Guide](/contributing/documentation-guide/) — Writing docs | |
0 | 80 | |
▼docs_site/src/content/docs/contributing/development-setup.md
+81-0
| @@ -0,0 +1,81 @@ | ||
1 | +--- | |
2 | +title: Development Setup | |
3 | +description: Set up your local environment for JustAPI development. | |
4 | +--- | |
5 | + | |
6 | +## Prerequisites | |
7 | + | |
8 | +- Python 3.11+ | |
9 | +- Rust 1.85+ (install via [rustup](https://rustup.rs/)) | |
10 | +- Git | |
11 | + | |
12 | +## Clone the Repository | |
13 | + | |
14 | +```bash | |
15 | +git clone https://github.com/swadhinbiswas/JustAPI.git | |
16 | +cd JustAPI | |
17 | +``` | |
18 | + | |
19 | +## Python Environment | |
20 | + | |
21 | +```bash | |
22 | +python -m venv .venv | |
23 | +source .venv/bin/activate | |
24 | +pip install maturin pydantic | |
25 | +``` | |
26 | + | |
27 | +## Build the Rust Extension | |
28 | + | |
29 | +```bash | |
30 | +maturin develop --release --manifest-path crates/justapi-py/Cargo.toml | |
31 | +``` | |
32 | + | |
33 | +For debug builds (faster compile, slower runtime): | |
34 | + | |
35 | +```bash | |
36 | +maturin develop --manifest-path crates/justapi-py/Cargo.toml | |
37 | +``` | |
38 | + | |
39 | +## Install CLI | |
40 | + | |
41 | +```bash | |
42 | +cargo install --path crates/justapi-cli | |
43 | +``` | |
44 | + | |
45 | +## Install All Dependencies | |
46 | + | |
47 | +```bash | |
48 | +# Rust | |
49 | +cargo build --workspace | |
50 | + | |
51 | +# Python | |
52 | +pip install -e ".[dev]" | |
53 | +``` | |
54 | + | |
55 | +## Editor Setup | |
56 | + | |
57 | +Recommended extensions: | |
58 | + | |
59 | +- **rust-analyzer** — Rust language support | |
60 | +- **pyright** or **pylance** — Python language support | |
61 | +- **maturin** — Python↔Rust integration | |
62 | + | |
63 | +## Running Tests | |
64 | + | |
65 | +```bash | |
66 | +# Rust tests | |
67 | +cargo test --workspace | |
68 | + | |
69 | +# Python tests | |
70 | +pytest | |
71 | + | |
72 | +# Linting | |
73 | +cargo clippy --workspace --tests -- -D warnings | |
74 | +cargo fmt --check | |
75 | +``` | |
76 | + | |
77 | +## Next Steps | |
78 | + | |
79 | +- [Coding Standards](/contributing/coding-standards/) — Code conventions and guidelines | |
80 | +- [Testing Guide](/contributing/testing-guide/) — How to write tests | |
81 | +- [Benchmarking Guide](/contributing/benchmarking-guide/) — Performance testing | |
0 | 82 | |
▼docs_site/src/content/docs/contributing/documentation-guide.md
+82-0
| @@ -0,0 +1,82 @@ | ||
1 | +--- | |
2 | +title: Documentation Guide | |
3 | +description: How to contribute to the JustAPI documentation site. | |
4 | +--- | |
5 | + | |
6 | +## Tech Stack | |
7 | + | |
8 | +The documentation site is built with: | |
9 | + | |
10 | +- **Astro** 7.x — Static site generator | |
11 | +- **Starlight** 0.41.x — Documentation theme | |
12 | +- **Pagefind** — Full-text search | |
13 | +- **Markdown/MDX** — Content format | |
14 | + | |
15 | +## Local Development | |
16 | + | |
17 | +```bash | |
18 | +cd docs_site | |
19 | +npm install | |
20 | +npm run dev | |
21 | +``` | |
22 | + | |
23 | +The development server starts at `http://localhost:4321`. | |
24 | + | |
25 | +## Adding a New Page | |
26 | + | |
27 | +1. Create a `.md` or `.mdx` file in the appropriate directory under `src/content/docs/` | |
28 | +2. Add frontmatter: | |
29 | + | |
30 | +```yaml | |
31 | +--- | |
32 | +title: My New Page | |
33 | +description: Brief description for SEO. | |
34 | +--- | |
35 | +``` | |
36 | + | |
37 | +3. If the page should appear in the sidebar, add it to `astro.config.mjs` in the `sidebar` array | |
38 | + | |
39 | +## Page Frontmatter | |
40 | + | |
41 | +| Field | Required | Description | | |
42 | +|---|---|---| | |
43 | +| `title` | Yes | Page title (also used in sidebar) | | |
44 | +| `description` | Yes | Meta description for SEO | | |
45 | +| `sidebar` | No | Custom sidebar label (defaults to title) | | |
46 | + | |
47 | +## Directory Structure | |
48 | + | |
49 | +``` | |
50 | +src/content/docs/ | |
51 | +├── index.mdx # Home page (splash layout) | |
52 | +├── getting-started/ # Getting started guides | |
53 | +├── tutorials/ # Step-by-step tutorials | |
54 | +├── api-reference/ # API documentation | |
55 | +├── advanced/ # Advanced guides | |
56 | +├── deployment/ # Deployment guides | |
57 | +├── security/ # Security documentation | |
58 | +├── observability/ # Observability docs | |
59 | +├── reference/ # Reference docs | |
60 | +├── contributing/ # Contributor guides | |
61 | +└── examples/ # Example gallery | |
62 | +``` | |
63 | + | |
64 | +## Conventions | |
65 | + | |
66 | +- Use `#` for page title (H1), `##` for sections (H2), `###` for subsections (H3) | |
67 | +- Code blocks should specify language for syntax highlighting: ` ```python ` | |
68 | +- Use relative links to other docs pages: `[Text](/getting-started/overview/)` | |
69 | +- All code examples should be tested before committing | |
70 | + | |
71 | +## Building | |
72 | + | |
73 | +```bash | |
74 | +npm run build # Output in dist/ | |
75 | +npm run preview # Preview the built site | |
76 | +``` | |
77 | + | |
78 | +## See Also | |
79 | + | |
80 | +- [Development Setup](/contributing/development-setup/) — Get started | |
81 | +- [Coding Standards](/contributing/coding-standards/) — Code conventions | |
82 | +- [Testing Guide](/contributing/testing-guide/) — Writing tests | |
0 | 83 | |
▼docs_site/src/content/docs/contributing/testing-guide.md
+152-0
| @@ -0,0 +1,152 @@ | ||
1 | +--- | |
2 | +title: Testing Guide | |
3 | +description: How to write and run tests for JustAPI. | |
4 | +--- | |
5 | + | |
6 | +## Running Tests | |
7 | + | |
8 | +```bash | |
9 | +# All Rust tests | |
10 | +cargo test --workspace | |
11 | + | |
12 | +# Single crate | |
13 | +cargo test -p justapi-core | |
14 | + | |
15 | +# Python tests | |
16 | +pytest | |
17 | + | |
18 | +# Miri (unsafe validation) | |
19 | +cargo +nightly miri test -p justapi-core | |
20 | +``` | |
21 | + | |
22 | +## Writing Rust Tests | |
23 | + | |
24 | +Unit tests go at the bottom of the module they test: | |
25 | + | |
26 | +```rust | |
27 | +#[cfg(test)] | |
28 | +mod tests { | |
29 | + use super::*; | |
30 | + | |
31 | + #[test] | |
32 | + fn test_normalize_db_url() { | |
33 | + assert_eq!(normalize_db_url("sqlite:///foo.db"), "sqlite:foo.db"); | |
34 | + assert_eq!(normalize_db_url("sqlite:////tmp/foo.db"), "sqlite:/tmp/foo.db"); | |
35 | + } | |
36 | +} | |
37 | +``` | |
38 | + | |
39 | +## Writing Python Tests | |
40 | + | |
41 | +Use the `JustAPITestClient` for in-process testing: | |
42 | + | |
43 | +```python | |
44 | +from justapi import JustAPIApp, JustAPITestClient | |
45 | + | |
46 | + | |
47 | +def test_hello_world(): | |
48 | + app = JustAPIApp() | |
49 | + | |
50 | + @app.get("/hello") | |
51 | + def hello(request): | |
52 | + return {"message": "Hello!"} | |
53 | + | |
54 | + client = JustAPITestClient(app) | |
55 | + response = client.get("/hello") | |
56 | + | |
57 | + assert response.status == 200 | |
58 | + assert response.json() == {"message": "Hello!"} | |
59 | +``` | |
60 | + | |
61 | +### Testing POST Endpoints | |
62 | + | |
63 | +```python | |
64 | +def test_create_item(): | |
65 | + app = JustAPIApp() | |
66 | + | |
67 | + @app.post("/items") | |
68 | + def create(request): | |
69 | + data = request.json() | |
70 | + return {"id": 1, **data} | |
71 | + | |
72 | + client = JustAPITestClient(app) | |
73 | + response = client.post("/items", body={"name": "Widget", "price": 9.99}) | |
74 | + | |
75 | + assert response.status == 200 | |
76 | + assert response.json()["name"] == "Widget" | |
77 | +``` | |
78 | + | |
79 | +### Testing Errors | |
80 | + | |
81 | +```python | |
82 | +def test_404(): | |
83 | + app = JustAPIApp() | |
84 | + client = JustAPITestClient(app) | |
85 | + response = client.get("/nonexistent") | |
86 | + assert response.status == 404 | |
87 | +``` | |
88 | + | |
89 | +## Integration Testing | |
90 | + | |
91 | +For integration tests that need a running server: | |
92 | + | |
93 | +```python | |
94 | +# tests/test_app.py | |
95 | +from justapi import JustAPIApp | |
96 | + | |
97 | +app = JustAPIApp() | |
98 | + | |
99 | +@app.get("/health") | |
100 | +def health(request): | |
101 | + return {"status": "ok"} | |
102 | +``` | |
103 | + | |
104 | +Then use `JustAPITestClient` for end-to-end flow tests. | |
105 | + | |
106 | +## Property-Based Testing | |
107 | + | |
108 | +Use `proptest` for Rust property-based tests: | |
109 | + | |
110 | +```rust | |
111 | +use proptest::prelude::*; | |
112 | + | |
113 | +proptest! { | |
114 | + #[test] | |
115 | + fn test_route_roundtrip(path in ".*") { | |
116 | + // Property: route matching should never panic | |
117 | + let router = Router::new(); | |
118 | + // ... | |
119 | + } | |
120 | +} | |
121 | +``` | |
122 | + | |
123 | +## Test File Layout | |
124 | + | |
125 | +``` | |
126 | +tests/ | |
127 | +├── test_annotations.py | |
128 | +├── test_middleware.py | |
129 | +├── test_responses.py | |
130 | +├── test_multipart.py | |
131 | +├── test_exceptions.py | |
132 | +├── test_db_concurrent.py | |
133 | +└── ... | |
134 | +``` | |
135 | + | |
136 | +## CI Testing | |
137 | + | |
138 | +Every PR runs: | |
139 | + | |
140 | +1. `cargo test --workspace` | |
141 | +2. `cargo clippy -- -D warnings` | |
142 | +3. `cargo fmt --check` | |
143 | +4. `cargo miri test -p justapi-core` | |
144 | +5. `pytest` (Python 3.12 + free-threaded 3.14t) | |
145 | +6. `cargo audit` | |
146 | +7. Fuzz tests (7 targets, 60s each) | |
147 | + | |
148 | +## See Also | |
149 | + | |
150 | +- [Development Setup](/contributing/development-setup/) — Get started | |
151 | +- [Coding Standards](/contributing/coding-standards/) — Code conventions | |
152 | +- [Testing Client API](/api-reference/testing-client/) — `JustAPITestClient` reference | |
0 | 153 | |