Functional AI is coming soon. Join the waitlist for early access.

fn: Functions, Not Prompts

June 25, 2026 · 6 min read · fnlabs

Most teams ship their first AI feature the same way: a prompt in a string, a model call behind it, and a hope that nobody touches it. It works in the demo. Then it goes to production, and the cracks show.

Someone tweaks the wording to fix one edge case and silently regresses three others. There is no record of what the prompt looked like last Tuesday, no way to compare two versions, and no number anyone trusts that says whether today's change is better or worse. The prompt is the most important code in the codebase, and it is the least engineered.

A prompt is not a unit of software

Functions have an interface. They have inputs and outputs with types. You can test them, version them, diff them, and roll them back. None of that is true of a prompt sitting in a string literal.

So we stopped treating prompts as the unit and made the function the unit instead. In fn, you write an fn.yml — a declarative file that names the inputs, the outputs, the model, and how the thing should be evaluated:

name: classify
version: 3
model: claude-opus-4-8

output:
  category: string
  priority: enum[low, high]
  confidence: number

eval:
  dataset: ./tickets.jsonl
  threshold: 0.95

Compile that and you get classify@v3: an immutable, content-addressed function with a typed API. The prompt is an implementation detail the runtime owns, not a string you babysit.

Versions are the whole point

Because every change produces a new immutable version, the questions that used to be unanswerable become trivial:

Rollback is instant because nothing is rebuilt. v2 still exists; you are only moving a pointer.

Evaluation is a gate, not a vibe

The reason prompt changes are scary is that nobody knows if they worked. We made evaluation a first-class command that exits non-zero below a threshold, so it drops straight into CI:

fn eval classify --dataset tickets.jsonl --threshold 0.95

If the score regresses, the build fails and the deploy never happens. A 2% drop that would have slipped past a human reviewer fails the pipeline instead.

Not an agent, either

We are deliberate about the words we avoid. fn is not an agent framework. It does not wander, plan, or call itself in a loop. It is a function: inputs in, structured output out, the same way every time, with the metadata you need to bill and trace it.

That constraint is the feature. A function you can test is a function you can trust in production — and trust is the thing that has been missing from AI features all along.

Ready to build one? Start with the Quickstart.