# Migration Strategies for Updating Ax Projects: From v13 Factory Functions to v15

> Update Ax projects from v13 to v15 seamlessly. Learn strategies to replace factory functions, adopt new classes, and ensure type safety for future compatibility.

- Repository: [Ax/ax](https://github.com/ax-llm/ax)
- Tags: migration-guide
- Published: 2026-02-25

---

**Migrate Ax projects from v13 to v15 by replacing template-literal signatures with factory functions, constructor-based classes with factory functions like `ai()` and `agent()`, and nested fluent helpers with pure-fluent chains to ensure type safety and future compatibility.**

Ax's evolution from v13.0.24 through v14.0.0 to v15.0.0 introduces a modern factory-function API and pure-fluent signature DSL that eliminate deprecated patterns. Understanding these migration strategies for updating Ax projects ensures your codebase maintains compatibility while gaining stronger compile-time guarantees and improved runtime performance.

## Deprecated Patterns and Modern Replacements

Ax v15 removes three legacy patterns in favor of cleaner, type-safe alternatives:

| Deprecated Pattern | New Replacement | Documentation |
|-------------------|----------------|---------------|
| **Template-literal signatures** (`s\`…\`` / `ax\`…\``) | Factory function calls (`s('…')` / `ax('…')`) | [[`docs/MIGRATION.md`](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md)](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md#template-literal-migration) |
| **Constructor-based classes** (`new AxAI()`, `new AxAgent()`, `new AxFlow()`, `new AxRAG()`) | Factory functions (`ai()`, `agent()`, `flow()`, `axRAG()`) | [[`docs/MIGRATION.md`](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md)](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md#constructor-migration) |
| **Nested fluent helpers** (`f.array(f.string())`, `f.optional(f.string())`) | Pure-fluent chains (`f.string().array()`, `f.string().optional()`) | [[`docs/MIGRATION.md`](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md)](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md#pure-fluent-api-updated-in-v14) |

## Step-by-Step Migration Workflow

Follow this systematic approach to update your Ax projects from v13 to v15:

1. **Identify deprecated usages** using ripgrep to locate legacy patterns:

```bash

# Find template literals

rg "ax\`[^`]*\`|s\`[^`]*\`" -t ts

# Find constructor calls

rg "new\s+Ax(AI|Agent|Flow|RAG)" -t ts

# Find nested fluent helpers

rg "f\.(array|optional|internal)\(" -t ts

```

2. **Execute automated replacements** for straightforward migrations:

```bash

# Template literals to factory functions

find . -name "*.ts" -exec sed -i 's/ax`\([^`]*\)`/ax("\1")/g' {} \;
find . -name "*.ts" -exec sed -i 's/s`\([^`]*\)`/s("\1")/g' {} \;

# Constructors to factory functions

find . -name "*.ts" -exec sed -i 's/new AxAI(/ai(/g' {} \;
find . -name "*.ts" -exec sed -i 's/new AxAgent(/agent(/g' {} \;
find . -name "*.ts" -exec sed -i 's/new AxRAG(/axRAG(/g' {} \;

# Nested helpers to pure-fluent chains

find . -name "*.ts" -exec sed -i 's/f\.array(/f.string().array(/g' {} \;
find . -name "*.ts" -exec sed -i 's/f\.optional(/f.string().optional(/g' {} \;
find . -name "*.ts" -exec sed -i 's/f\.internal(/f.string().internal(/g' {} \;

```

3. **Update import statements** to reference factory functions:

```bash
find . -name "*.ts" -exec sed -i 's/import { AxAI }/import { ai }/g' {} \;
find . -name "*.ts" -exec sed -i 's/import { AxAgent }/import { agent }/g' {} \;

```

4. **Refactor dynamic signatures** manually using the fluent `f()` builder for runtime-determined field types.

5. **Validate the migration** by running the test suite:

```bash
npm run test --workspace=@ax-llm/ax

```

6. **Iterate** on any compilation errors until the suite passes.

## Architectural Rationale for the New API

The shift to factory functions and pure-fluent chains in Ax v14+ delivers concrete technical advantages:

- **Factory functions** (`ai()`, `agent()`, `flow()`) provide the compiler with complete knowledge of concrete LLM configurations at the call site. This improves IntelliSense accuracy and ensures model-specific options are correctly typed according to the source code in [`src/ax/index.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/index.ts).

- **Pure-fluent DSL** eliminates runtime parsing overhead. Previously, nested helpers like `f.array(f.string())` required order-dependent resolution. The new chainable API (`f.string().array().optional()`) resolves statically, removing that runtime cost as implemented in the core library.

- **Consistent entry points** standardize every top-level object under the same "create-then-use" pattern, simplifying onboarding and future provider extensions.

## Common Migration Pitfalls and Fixes

| Issue | Symptom | Resolution |
|-------|---------|------------|
| **Variable name clash** | `const ai = ai(...)` shadows the factory function, causing "`ai is not a function`" errors | Rename the instance (e.g., `const llm = ai(...)`) as documented in the "Variable Naming Conflicts" section of [`docs/MIGRATION.md`](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md) |
| **Dynamic field types** | TypeScript infers `any` for dynamically constructed fields | Use the fluent `f()` builder where field types are determined at runtime (see "Template Literal Field Interpolation" in the migration guide) |
| **Missing import updates** | Compile-time error: "cannot find name 'AxAI'" | Ensure all old class imports are swapped for factory functions (`ai`, `agent`, etc.) exported from [`src/ax/index.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/index.ts) |

## Before and After Code Examples

### Agent Implementation

**Pre-migration (v13 style):**

```typescript
import { AxAI, AxAgent } from "@ax-llm/ax";

const ai = new AxAI({ name: "openai", apiKey: process.env.OPENAI_APIKEY! });
const sig = s`question:string -> answer:string`;
const agent = new AxAgent({ name: "helper", signature: sig, ai });

```

**Post-migration (v14+ style):**

```typescript
import { ai, s, agent, f } from "@ax-llm/ax";

const llm = ai({ name: "openai", apiKey: process.env.OPENAI_APIKEY! });

// Fluent signature (preferred for dynamic fields)
const sig = f()
  .input("question", f.string("User query"))
  .output("answer", f.string("Generated answer"))
  .build();

const helper = agent({
  name: "helper",
  signature: sig,
  ai: llm,
});

```

### RAG Implementation

**Deprecated class-based approach:**

```typescript
import { AxRAG, AxAI } from "@ax-llm/ax";

const ai = new AxAI({ name: "openai", apiKey: "…" });
const rag = new AxRAG({ ai, db: vectorDb });

```

**Modern factory function approach:**

```typescript
import { ai, axRAG } from "@ax-llm/ax";

const llm = ai({ name: "openai", apiKey: "…" });
const rag = axRAG({ ai: llm, db: vectorDb });

```

## Key Reference Files

| File | Purpose |
|------|---------|
| [[`docs/MIGRATION.md`](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md)](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md) | Authoritative guide covering all deprecated patterns and migration scripts |
| [[`src/examples/agent-migration-example.ts`](https://github.com/ax-llm/ax/blob/main/src/examples/agent-migration-example.ts)](https://github.com/ax-llm/ax/blob/main/src/examples/agent-migration-example.ts) | Concrete, up-to-date example of a migrated agent implementation |
| [[`src/docs/src/content/docs/migration.md`](https://github.com/ax-llm/ax/blob/main/src/docs/src/content/docs/migration.md)](https://github.com/ax-llm/ax/blob/main/src/docs/src/content/docs/migration.md) | Documentation site source for the migration guide |
| [`src/ax/index.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/index.ts) | Core library entry point showing exported factory functions (`ai`, `agent`, `ax`, `f`) |
| `src/ax/**/*.test.ts` | Test suite for validating migrated code against type contracts |

## Summary

- **Factory functions replace constructors**: Swap `new AxAI()`, `new AxAgent()`, and `new AxRAG()` with `ai()`, `agent()`, and `axRAG()` to enable better type inference and IntelliSense.
- **Function calls replace template literals**: Convert `` s`...` `` and `` ax`...` `` to `s('...')` and `ax('...')` for static signatures, or use the fluent `f()` builder for dynamic fields.
- **Pure-fluent chains replace nested helpers**: Transform `f.array(f.string())` into `f.string().array()` to eliminate runtime parsing overhead and enable static resolution.
- **Automated migration is available**: Use the provided `find` and `sed` commands to bulk-update most patterns, then run `npm run test --workspace=@ax-llm/ax` to validate.
- **Avoid variable shadowing**: Never name your instance `const ai = ai(...)`; use descriptive names like `const llm = ai(...)` instead.

## Frequently Asked Questions

### How do I handle dynamic signature fields that were built with template literals?

For signatures where field types are determined at runtime, replace the template literal approach with the fluent `f()` builder API. Instead of constructing strings dynamically, use `f().input()` and `f().output()` with type-safe methods like `f.string()`, `f.number()`, and their chainable modifiers (`.array()`, `.optional()`). This approach is documented in the "Template Literal Field Interpolation" section of [`docs/MIGRATION.md`](https://github.com/ax-llm/ax/blob/main/docs/MIGRATION.md).

### What is the correct way to rename variables when migrating from constructors to factory functions?

When migrating from `new AxAI()` to `ai()`, avoid naming the resulting instance `ai` as this shadows the factory function import. Instead, use descriptive variable names that indicate the provider or purpose, such as `const llm = ai({ name: "openai", ... })` or `const openaiClient = ai(...)`. This prevents "ai is not a function" errors when you attempt to create additional instances later in the same scope.

### Are there automated tools to verify my migration is complete?

Yes, after applying the automated `find` and `sed` replacements, run the comprehensive test suite using `npm run test --workspace=@ax-llm/ax`. This executes the library's type contracts and runtime assertions against your migrated code. Additionally, use `rg` (ripgrep) commands to search for remaining deprecated patterns like `new AxAI`, `` ax` ``, or `f.array(` to ensure no legacy code remains.

### Why were constructor-based classes removed in favor of factory functions?

The shift to factory functions (`ai()`, `agent()`, `axRAG()`) enables the TypeScript compiler to infer concrete LLM configuration types at the call site, providing accurate IntelliSense and ensuring model-specific options are correctly typed. According to the source code in [`src/ax/index.ts`](https://github.com/ax-llm/ax/blob/main/src/ax/index.ts), this pattern standardizes the "create-then-use" approach across all top-level objects, simplifying the API surface and eliminating the runtime overhead associated with class instantiation and `this` binding.