Skip to main content
This guide covers version upgrades, breaking changes, and best practices for keeping your chatbot up to date.

Version management

The chatbot uses semantic versioning and follows the AI SDK’s release cycle. Major changes are coordinated with AI SDK updates.
Always review the changelog and test upgrades in a development environment before deploying to production.

Upgrade checklist

1

Update dependencies

Update your package.json dependencies to the latest versions:
pnpm update
Pay special attention to these core dependencies:
  • ai - The AI SDK
  • next - Next.js framework
  • drizzle-orm - Database ORM
  • @vercel/functions - Vercel runtime utilities
2

Review breaking changes

Check the release notes for breaking changes. Common areas that may require updates:
  • Database schema changes
  • API route signatures
  • Environment variable names
  • AI SDK streaming API changes
  • Tool definition structures
3

Run database migrations

Apply any new database migrations:
pnpm db:migrate
This ensures your database schema matches the latest version.
4

Update environment variables

Review .env.example for new or changed environment variables:
# Compare your .env with .env.example
diff .env .env.example
5

Test in development

Run the development server and test key functionality:
pnpm dev
Test these critical paths:
  • User authentication
  • Chat creation and streaming
  • Tool calls and approvals
  • Document creation and editing
  • Rate limiting

Major version migrations

Message parts migration

One of the most significant changes is the migration from content-based to parts-based messages.
The Message and Vote tables are deprecated. Migrate to Message_v2 and Vote_v2 before they are removed.
See the message parts migration guide for detailed instructions.

AI SDK v4 changes

The chatbot uses AI SDK v4, which introduced several breaking changes:
Tools now support a needsApproval flag for requiring user confirmation:
lib/ai/tools/get-weather.ts
export const getWeather = tool({
  description: "Get the current weather at a location.",
  inputSchema: z.object({
    latitude: z.number().optional(),
    longitude: z.number().optional(),
    city: z.string().optional(),
  }),
  needsApproval: true,
  execute: async (input) => {
    // Tool implementation
  },
});
The streaming API now uses createUIMessageStream for better control:
app/(chat)/api/chat/route.ts
const stream = createUIMessageStream({
  originalMessages: isToolApprovalFlow ? uiMessages : undefined,
  execute: async ({ writer: dataStream }) => {
    const result = streamText({
      model: getLanguageModel(selectedChatModel),
      system: systemPrompt({ selectedChatModel, requestHints }),
      messages: modelMessages,
      tools: {
        getWeather,
        createDocument: createDocument({ session, dataStream }),
        updateDocument: updateDocument({ session, dataStream }),
        requestSuggestions: requestSuggestions({ session, dataStream }),
      },
    });

    dataStream.merge(
      result.toUIMessageStream({ sendReasoning: isReasoningModel })
    );
  },
});

Database schema evolution

The database schema evolves over time. Here’s how to handle schema changes:

Adding new fields

When new fields are added to existing tables:
// New field added to chat table
export const chat = pgTable("Chat", {
  id: uuid("id").primaryKey().notNull().defaultRandom(),
  createdAt: timestamp("createdAt").notNull(),
  title: text("title").notNull(),
  userId: uuid("userId")
    .notNull()
    .references(() => user.id),
  visibility: varchar("visibility", { enum: ["public", "private"] })
    .notNull()
    .default("private"),
});

Creating new tables

New features may introduce new tables like the Stream table for resumable streams:
lib/db/schema.ts
export const stream = pgTable(
  "Stream",
  {
    id: uuid("id").notNull().defaultRandom(),
    chatId: uuid("chatId").notNull(),
    createdAt: timestamp("createdAt").notNull(),
  },
  (table) => ({
    pk: primaryKey({ columns: [table.id] }),
    chatRef: foreignKey({
      columns: [table.chatId],
      foreignColumns: [chat.id],
    }),
  })
);

Configuration updates

Model provider changes

As new AI providers are added, update your model configuration:
// Check allowedModelIds for newly supported models
if (!allowedModelIds.has(selectedChatModel)) {
  return new ChatbotError("bad_request:api").toResponse();
}

Rate limiting adjustments

Rate limits can be adjusted based on user type:
lib/ai/entitlements.ts
export const entitlementsByUserType: Record<UserType, Entitlements> = {
  guest: {
    maxMessagesPerDay: 10,
  },
  regular: {
    maxMessagesPerDay: 10,
  },
  // Add new tiers as needed
};

API changes

Request validation

API routes now use Zod schemas for request validation:
try {
  const json = await request.json();
  requestBody = postRequestBodySchema.parse(json);
} catch (_) {
  return new ChatbotError("bad_request:api").toResponse();
}

Error handling

The error handling system uses typed error codes:
lib/errors.ts
export type ErrorCode = `${ErrorType}:${Surface}`;

export class ChatbotError extends Error {
  type: ErrorType;
  surface: Surface;
  statusCode: number;

  constructor(errorCode: ErrorCode, cause?: string) {
    super();
    const [type, surface] = errorCode.split(":");
    this.type = type as ErrorType;
    this.cause = cause;
    this.surface = surface as Surface;
    this.message = getMessageByErrorCode(errorCode);
    this.statusCode = getStatusCodeByType(this.type);
  }

  toResponse() {
    const code: ErrorCode = `${this.type}:${this.surface}`;
    return Response.json({ code, message: this.message }, { status: this.statusCode });
  }
}

Common upgrade issues

If you encounter TypeScript errors after upgrading:
  1. Clear your build cache: rm -rf .next
  2. Reinstall dependencies: rm -rf node_modules && pnpm install
  3. Restart your TypeScript server in your editor
  4. Check for breaking changes in the AI SDK changelog
If database queries fail after migration:
  1. Verify POSTGRES_URL is set correctly
  2. Run migrations: pnpm db:migrate
  3. Check that new tables were created
  4. Verify foreign key relationships are intact
If streaming responses break:
  1. Check that you’re using createUIMessageStream
  2. Verify maxDuration is set on API routes
  3. Ensure toUIMessageStream() is called on the result
  4. Check that data stream is properly merged

Rollback strategy

If you need to rollback an upgrade:
1

Database backup

Always backup your database before major upgrades:
# Using pg_dump for Postgres
pg_dump $POSTGRES_URL > backup.sql
2

Git tags

Tag your releases to make rollback easier:
git tag -a v1.0.0 -m "Release version 1.0.0"
git push origin v1.0.0
3

Revert code

Checkout the previous version:
git checkout v1.0.0
pnpm install
4

Restore database

If needed, restore the database backup:
psql $POSTGRES_URL < backup.sql

Next steps

Build docs developers (and LLMs) love