mrapids 0.1.31

Your OpenAPI, but executable
Documentation
# Architecture Decisions for API-Runtime

## Core Principles (Just 5)

1. **Boring is Good** - Use obvious solutions, not clever ones
2. **One Thing Well** - Each function/module does ONE thing
3. **Fail Loudly** - Errors should scream what went wrong
4. **Test the Behavior** - Not the implementation
5. **Delete, Don't Comment** - Dead code dies

## The 3-3-3 Rule

### 3 Layers Maximum
```
CLI → Service → Core
(Input → Logic → Output)
```

### 3 Parameters Maximum
```rust
// ✅ Good
fn execute(spec: &Spec, operation: &str, config: &Config)

// ❌ Bad - use a struct
fn execute(spec: &Spec, op: &str, timeout: u64, retry: u32, parallel: bool)
```

### 3 Levels Deep Maximum
```rust
// ✅ Good - flat
if !valid { return Err(...) }
process();
return Ok(result);

// ❌ Bad - nested
if valid {
    if authorized {
        if has_data {
            process();
        }
    }
}
```

## Naming: The Only Convention

### Be Boring and Obvious
```rust
// ✅ Obvious what it does
load_spec_from_file()
execute_http_request()
parse_json_response()

// ❌ Clever or vague
process_data()
handle_stuff()
do_the_thing()
```

### Tell Me What, Not How
```rust
// ✅ What it does
validate_response()
find_operation()

// ❌ How it does it
loop_through_array()
use_hashmap_lookup()
```

## Just Build It Like This

### Every Module Pattern
```rust
// errors.rs - What can go wrong
pub enum ModuleError {
    NotFound(String),
    Invalid(String),
}

// types.rs - What we work with
pub struct Thing {
    pub id: String,
}

// mod.rs - What we do
pub fn do_something(thing: &Thing) -> Result<Output> {
    // 1. Validate
    // 2. Process  
    // 3. Return
}

// tests.rs - Prove it works
#[test]
fn it_works() {
    assert!(do_something(&thing).is_ok());
}
```

### Every Error Message
```rust
// ✅ Tells me how to fix it
"Cannot find spec 'api.yaml'. Did you mean 'api.yml'?"
"Port 3000 is in use. Try: --port 3001"
"Invalid JSON at line 5. Expected ',' or '}'."

// ❌ Useless
"Error occurred"
"Invalid input"
"Failed to process"
```

## Test Only What Matters

### Test Behavior, Not Code
```rust
// ✅ Tests what users care about
#[test]
fn returns_404_for_unknown_operation() {
    let result = runtime.execute("unknown_op");
    assert_eq!(result.status(), 404);
}

// ❌ Tests implementation details
#[test]
fn uses_hashmap_internally() {
    // Who cares?
}
```

## What NOT to Do (Just 5 Things)

1. **Don't Abstract Too Early** - Wait until you have 3 examples
2. **Don't Nest Beyond 3** - Use early returns
3. **Don't Comment Obvious Code** - Make code obvious instead
4. **Don't Catch and Hide** - Let errors bubble with context
5. **Don't Optimize Prematurely** - Wait for real metrics

## The One-Screen Rule

If you can't see the whole function on one screen, it's too big.
Split it up.

## When Stuck, Ask:

1. Can I delete this?
2. Can I make this simpler?
3. Will I understand this in 6 months?

If any answer is "no", refactor now.

## The Minimal `.rustfmt.toml`

```toml
# Just 3 rules that matter
max_width = 100              # Fits on screen
imports_granularity = "Crate" # Consistent imports  
group_imports = "StdExternalCrate" # Logical grouping
```

## The Essential Clippy

```rust
// In main.rs - just these matter
#![warn(clippy::unwrap_used)]   // No panics
#![warn(clippy::print_stdout)]  // Use logging
#![warn(clippy::todo)]          // No TODOs in prod
```

## The Real Secret: Start Ugly, Refactor Later

```rust
// Version 1: Just make it work
fn process(data: String) -> String {
    // Ugly but works
    data.to_uppercase()
}

// Version 2: Make it right (after it works)
fn process(data: impl AsRef<str>) -> Result<String> {
    Ok(data.as_ref().to_uppercase())
}

// Version 3: Make it fast (after profiling)
// Only if needed!
```

## The Architecture That Always Works

```
src/
├── main.rs        # Just wiring
├── cli.rs         # Just parsing  
├── app.rs         # Just orchestration
├── core/          # The actual logic
│   ├── spec.rs    # Spec handling
│   ├── runtime.rs # Execution
│   └── tests.rs   # Behavior tests
└── utils/         # Boring helpers
```

## How API-Runtime Actually Works (v0.1.0)

### Data Flow (Keep It Simple)
```
1. User runs: mrapids run api.yaml --operation getUser
2. CLI parses arguments
3. Load OpenAPI spec from file
4. Find the operation
5. Build HTTP request
6. Execute request
7. Display response
```

### Core Components (Just What We Need)
```rust
// spec.rs - Just load and parse
pub fn load_spec(path: &str) -> Result<Spec, Error> {
    // Read file, parse YAML/JSON, return spec
}

// runtime.rs - Just execute
pub fn execute(spec: &Spec, operation: &str) -> Result<Response, Error> {
    // Find operation, build request, send it
}

// cli.rs - Just parse args
pub fn parse_args() -> Args {
    // Use clap, return args
}
```

### What We're NOT Building (Yet)
- Plugin systems (wait for v0.5)
- Caching layers (wait for performance issues)
- Abstract interfaces (wait for second implementation)
- Retry mechanisms (wait for user complaints)

### When to Add Complexity
Only add architecture when:
1. Users complain about missing features (not before)
2. Performance metrics show real problems (not imagined)
3. You have 3+ examples of the same pattern (not 1)

## Decision Framework

```mermaid
graph TD
    A[New Feature] --> B{Will I use it 3+ times?}
    B -->|Yes| C[Build abstraction]
    B -->|No| D[Copy-paste for now]
    
    D --> E{Used 3 times?}
    E -->|Yes| C
    E -->|No| D
```

## The Golden Rule

**Code is for humans. Make it boring.**

Boring code is:
- Easy to read at 3 AM
- Easy to debug
- Easy to delete
- Easy to replace

## That's It. Nothing More.

Don't add more rules. These cover 95% of what matters.

The other 5%? You'll know it when you see it.

---

Put this in your project as ARCHITECTURE.md and follow it. Delete everything else.

Remember: **Perfect is the enemy of shipped.**