# Performance Optimization Strategies in Agent-Native: 7 Techniques for Sub-Second Apps

> Learn 7 performance optimization strategies in Agent-Native for sub-second apps. Discover techniques for fast list loads and real-time sync without database tuning.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: performance
- Published: 2026-06-28

---

**Agent-Native enforces a comprehensive performance skill that applies seven core optimizations—including column projection, hot-path indexing, and N+1 query prevention—to guarantee fast list loads and real-time sync without manual database tuning.**

The `BuilderIO/agent-native` repository ships with a declarative performance skill defined in [`.agents/skills/performance/SKILL.md`](https://github.com/BuilderIO/agent-native/blob/main/.agents/skills/performance/SKILL.md) that automatically activates whenever you add a data model, list, or read path. These rules work across SQLite, Postgres, and managed SQL backends to eliminate common bottlenecks before they reach production.

## Core Database Optimizations

### Project Only Needed Columns

Never use `SELECT *` on lists. Instead, truncate heavy text columns at the database level to reduce network payload and memory pressure.

In your Drizzle ORM queries, explicitly select only the columns required for the UI. According to the skill file (lines 41–50), you should truncate large fields using native SQL functions:

```typescript
import { sql } from 'drizzle-orm';
import { db, docs } from '@/server/db';

const rows = await db
  .select({
    id: docs.id,
    title: docs.title,
    updatedAt: docs.updatedAt,
    // Truncate content to 400 chars to avoid loading blobs
    preview: sql<string>`substr(${docs.content}, 1, 400)`,
  })
  .from(docs)
  .where(accessFilter(docs, docShares))
  .orderBy(desc(docs.updatedAt));

```

### Index Hot-Path Columns

Add database indexes for every column used in filters, sorts, foreign keys, and share tables. This turns full-table scans into fast index lookups as tables grow.

Declare indexes idempotently in your migration array within [`server/plugins/db.ts`](https://github.com/BuilderIO/agent-native/blob/main/server/plugins/db.ts) (as documented in lines 63–88 of the skill):

```typescript
export const migrations = [
  // Idempotent index creation
  sql`
    CREATE INDEX IF NOT EXISTS forms_owner_org_updated_idx
    ON forms (owner_email, org_id, updated_at)
  `,
];

```

### Avoid N+1 Queries

Batch or parallelize data fetching instead of looping over individual queries. This cuts database round-trips from *N* to 1.

When loading child records for many parent items, fetch all children in a single query using `inArray`, then group in memory:

```typescript
// Get all post IDs first
const postIds = posts.map(p => p.id);

// Single query for all comments
const comments = await db
  .select()
  .from(commentsTable)
  .where(inArray(commentsTable.postId, postIds));

// Group by postId in application code
const commentsByPost = groupBy(comments, 'postId');

```

## Client-Side Rendering Optimizations

### Prevent Client-Side Waterfalls

Fire independent queries in parallel rather than gating one query on another’s result. The skill (lines 102–106) recommends using `useActionQuery` hooks that execute concurrently:

```tsx
import { useActionQuery } from '@agent-native/core';

function Dashboard() {
  // Both queries run in parallel; no waterfall
  const { data: summary } = useActionQuery('getSummary');
  const { data: recent } = useActionQuery('listRecentItems');

  if (!summary || !recent) return <LoadingSpinner />;

  return <DashboardView summary={summary} recent={recent} />;
}

```

### Virtualize Long Rendered Lists

Render only visible rows to stop the browser from re-rendering thousands of DOM nodes on every update. This strategy is documented in the "Virtualize" section of the skill file and applies to unbounded collections.

### Paginate and Window Unbounded Lists

Never load an entire history. Fetch a recent window (e.g., last 50 items) and request more on demand to guarantee O(1) load time regardless of collection size.

## Real-Time and Polling Strategies

### Cheap Polling and Compute-Once

Rely on real-time sync for updates rather than aggressive polling. When polling is necessary, use wide intervals and move heavy computation to write-time.

The performance skill recommends combining `useDbSync` with extended refetch intervals:

```typescript
// 1-minute interval backup polling; primary updates via SSE/WebSocket
useDbSync('messages', { refetchInterval: 60_000 });

```

The real-time sync mechanism—detailed in `packages/core/docs/content/real-time-collaboration.mdx`—pushes updates via SSE/WebSocket, eliminating the need for expensive repeated reads.

## Supporting Skills and Architecture

These performance rules integrate with two complementary skills:

- **storing-data**: Defines schema migrations and database setup where indexes are declared
- **real-time-sync**: Provides the live update mechanism that reduces polling requirements

Together, these files ensure that optimizations apply consistently across the `BuilderIO/agent-native` architecture, from database migrations in [`server/plugins/db.ts`](https://github.com/BuilderIO/agent-native/blob/main/server/plugins/db.ts) to client rendering in components like [`packages/core/src/client/StarfieldBackground.tsx`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/StarfieldBackground.tsx).

## Summary

- **Project specific columns** using Drizzle ORM selectors and SQL truncation to minimize payload size
- **Index hot paths** via idempotent migrations in [`server/plugins/db.ts`](https://github.com/BuilderIO/agent-native/blob/main/server/plugins/db.ts) for filters, sorts, and foreign keys
- **Eliminate N+1 queries** by batching fetches with `inArray` and grouping results in memory
- **Prevent waterfalls** by firing independent `useActionQuery` calls in parallel
- **Virtualize and paginate** large lists to maintain O(1) rendering and load times
- **Poll cheaply** using wide intervals and rely on real-time sync for immediate updates
- **Compute once** at write-time rather than repeatedly at read-time

## Frequently Asked Questions

### How does Agent-Native prevent N+1 query problems?

Agent-Native prevents N+1 queries by enforcing batch fetching patterns documented in [`.agents/skills/performance/SKILL.md`](https://github.com/BuilderIO/agent-native/blob/main/.agents/skills/performance/SKILL.md). Instead of querying inside a loop, you fetch all related records in a single query using `inArray` filters, then group the results in application code. This reduces database round-trips from *N* to 1.

### What is the recommended way to handle large text columns in list views?

The performance skill requires truncating large text columns at the database level using native SQL functions like `substr()`. In your Drizzle queries, never select the full column for list views; instead project a preview (e.g., first 400 characters) to avoid loading heavy blobs into memory and across the network.

### Where should I add database indexes in an Agent-Native project?

Declare indexes in the migrations array within [`server/plugins/db.ts`](https://github.com/BuilderIO/agent-native/blob/main/server/plugins/db.ts) using idempotent SQL statements like `CREATE INDEX IF NOT EXISTS`. The performance skill specifies that you should index every column used for filtering, sorting, foreign keys, and share tables to ensure hot paths remain fast as data grows.

### How does Agent-Native maintain real-time updates without expensive polling?

Agent-Native leverages the real-time sync mechanism described in `packages/core/docs/content/real-time-collaboration.mdx`, which pushes updates via SSE or WebSocket. While the system supports polling via `useDbSync`, the recommendation is to use wide intervals (e.g., 60 seconds) as a backup only, relying on the push mechanism for immediate updates.