Skip to content

When NOT to Use Criterion

Criterion is intentionally limited in scope. Understanding what it's not designed for will save you time and frustration.

What Criterion Is NOT

Not a Workflow Engine

If you need to orchestrate multi-step processes with retries, timeouts, and state persistence:

typescript
// ❌ NOT Criterion's job
await workflow()
  .step("validate", validateOrder)
  .step("charge", chargePayment)
  .step("fulfill", shipOrder)
  .onFailure("refund", issueRefund)
  .run();

Use instead:

Criterion's role: Make a single decision within a workflow step.

Not a Machine Learning Framework

If you need probabilistic predictions, model training, or neural networks:

typescript
// ❌ NOT Criterion's job
const score = model.predict(userFeatures);
const recommendation = rankItems(userHistory);

Use instead:

  • scikit-learn, TensorFlow, PyTorch — ML training
  • Feature stores — Feature engineering
  • MLflow, Kubeflow — ML ops

Criterion's role: Apply thresholds to ML scores (if score > threshold → action).

Not a Data Pipeline

If you need ETL, streaming, or data transformation:

typescript
// ❌ NOT Criterion's job
pipeline()
  .extract(database)
  .transform(cleanData)
  .load(dataWarehouse)

Use instead:

Criterion's role: A transformation step that applies business rules to each record.

Not a Rules Marketplace

Criterion doesn't provide:

  • Pre-built rule templates
  • Industry-specific rule packs
  • Drag-and-drop rule builders
  • Visual rule editors for business users

Why: These add complexity and opinionated abstractions. Criterion stays small so you build exactly what you need.

Not a No-Code Platform

Criterion is for developers. It requires:

  • TypeScript/JavaScript knowledge
  • Understanding of your business domain
  • Code deployment process

Business users can:

  • Read decision explanations
  • Review audit logs
  • Adjust profile parameters (if you build that UI)

But they cannot create or modify rules without code changes.

When Simple If/Else Is Enough

Don't over-engineer. Use plain conditionals when:

1. Logic Is Trivial

typescript
// Just use if/else
if (user.age >= 18) {
  return "allowed";
}

Criterion adds value when you have 5+ rules interacting, not for single checks.

2. No Audit Requirements

If no one will ever ask "why did this happen?", explanations are overhead.

3. Logic Never Varies

If the same rules apply everywhere (no profiles needed), the abstraction may not pay off.

4. You Control All the Code

If it's a personal project or small team with no compliance needs, direct code is simpler.

Decision Tree

Do you need audit/explanation?
├── No → Consider plain if/else
└── Yes
    └── Does logic vary by context (region/tier/client)?
        ├── No → Consider plain if/else with logging
        └── Yes
            └── Do you have 5+ interacting rules?
                ├── No → Consider plain if/else
                └── Yes → Criterion is a good fit

Common Misconceptions

"Criterion replaces all business logic"

No. It handles decisions, not all logic. Data fetching, side effects, and I/O happen outside.

"Criterion is faster than if/else"

No. It's actually slightly slower due to schema validation and trace generation. But:

  • ~1M evaluations/sec is fast enough for most use cases
  • The overhead enables debugging and auditing
  • The real win is maintainability, not raw speed

"Criterion works with any data store"

Criterion is pure — it doesn't know about databases. You:

  1. Fetch data from your store
  2. Pass it to Criterion
  3. Store the result if needed

"Profiles are a database"

Profiles are configuration, not data storage. They're typically:

  • Loaded at startup
  • Cached in memory
  • Changed via deployments or config systems

If you need dynamic per-request profiles, you load them yourself and pass them in.

Hybrid Approaches

Criterion works well alongside other tools:

typescript
// ML score + business rules
const mlScore = await model.predict(features);
const decision = engine.run(riskDecision, {
  ...input,
  mlScore,  // Pass ML output as input
}, { profile });
typescript
// Workflow step using Criterion
workflow.step("approve", async (ctx) => {
  const decision = engine.run(approvalDecision, ctx.data, { profile });
  if (decision.data.approved) {
    return ctx.next("process");
  }
  return ctx.next("review");
});

Summary

NeedTool
Multi-step workflowsTemporal, Inngest
ML predictionsscikit-learn, TensorFlow
Data pipelinesAirflow, Dagster
Visual rule builderCommercial BRM tools
Auditable business decisionsCriterion
Simple single conditionsPlain if/else

Next Steps

Released under the MIT License.