Magic vs. Control ​
In framework design, there is a constant tug-of-war between "Magic" (doing things automatically for the developer) and "Control" (forcing the developer to wire everything up manually).
- If a framework is too magical, it becomes an un-debuggable black box the moment you hit an edge case.
- If a framework is too explicit, you end up writing thousands of lines of Kubernetes manifests and networking boilerplate just to get two microservices to talk to each other.
Carotene solves this through Progressive Disclosure of Complexity, applying strict Convention over Configuration.
Maximum Magic (The 90%) ​
Carotene's Inference Engine reads your Abstract Syntax Tree (AST) and makes highly educated guesses about your infrastructure based on the environment and the verbs you use.
For 90% of standard application development, you write zero infrastructure boilerplate.
backend {
store {
model Order { id: UUID, total: Float }
}
function PlaceOrder() {
requires Customer
creates store.Order
}
}
frontend {
view Checkout() {
calls backend.PlaceOrder
}
}What the Carotene compiler infers from this "magic" block:
- The Database: Because
storeis inside abackend, it infers durable storage. It defaults to generating a Postgres database with Prisma/Drizzle schemas. - The Networking: Because the frontend
callsthe backend synchronously, it infers standard JSON-RPC over HTTPS. It generates the internal fetch client automatically. - The Security: Because
PlaceOrderrequires theCustomerrole, the generated frontend client automatically injects the user's JWT into theAuthorizationheader, and the backend middleware validates it before the AI's logic even runs.
Maximum Control (The 10%) ​
When you need to step outside the standard CRUD paradigm—whether you are building a 60fps game loop, an IoT edge device, or an authoritative UDP server—the "magic" defaults are no longer sufficient.
You take back absolute control using Typed config Blocks.
The config block is Carotene's universal escape hatch. By dropping a config block into any primitive, you override the Inference Engine with explicit instructions. Because these blocks are typed, the compiler provides strict schema validation for your overrides.
Example 1: Overriding Default Storage ​
If you are building an iOS app, the default frontend store is SQLite (for offline caching). If you want to use the device's secure enclave instead, you explicitly type the config:
frontend {
config ios { targetVersion: "17.0" }
store {
// Overriding SQLite with the iOS Secure Keychain
config keychain { accessible: "WhenUnlocked" }
model SessionToken { token: String }
}
}Example 2: The Service Mesh & Transport Layer ​
If you have a backend execution loop that needs to broadcast player positions to connected clients 30 times a second, HTTP is too slow. You can override the internal Service Mesh transport protocol directly on the loop:
backend AuthoritativeServer {
loop ServerTick {
// Bypassing HTTP entirely for this specific loop
config network {
rate: "30hz"
transport: "udp"
}
mutates state.Match
emits "network.state_sync"
}
}Example 3: Hardware Integrations ​
Integrations use typed config blocks to dictate exactly what kind of third-party system the generated code must interface with. The compiler ensures you provide the correct parameters.
integration TemperatureSensor {
// The compiler strictly validates I2C properties
config i2c {
address: "0x76"
pins: [4, 5]
}
function ReadContinuous() -> Stream<Float>
}The Verdict: No Lock-In ​
With Carotene, you get the rapid prototyping speed of a "magic" framework, without the terrifying vendor lock-in.
You can build your MVP using the implicit defaults. Months later, when your system scales and your database becomes a bottleneck, you don't need to rewrite your application. You simply open your .carrot file, drop a config block into your store to point it at a specialized cluster, re-compile, and let Carotene generate the new infrastructure.
Your AI-written business logic never has to change.