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

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 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:

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 (as documented in lines 63–88 of the skill):

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:

// 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:

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:

// 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 to client rendering in components like 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 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. 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.

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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →