Blogs

Welcome to my blog domain where I share personal stories about things I've learned, projects I'm hacking on and just general findings. I also write for other publications.

Blog Banner

AI Agentic Workflows

In the rapidly evolving landscape of artificial intelligence, agentic workflows empower developers to create sophisticated, autonomous systems that leverage large language models (LLMs) with enhanced capabilities like tool calling and task orchestration. These workflows provide structured approaches to break down complex tasks, route queries efficiently, parallelize operations, coordinate multi-step processes, and iteratively optimize outputs. This article explores five foundational agentic workflow patterns, complete with practical examples, to help developers design robust AI agents for real-world applications.


1. Prompt Chaining

When to Use:

  • Structured multi-step tasks
  • Where outputs of one step feed the next

Example: Generate a commit message and validate it

import { generateText } from "ai";

async function generateCommitMessage(diff) {
  const raw = await generateText({
    model: "gpt-4",
    prompt: `Write a conventional commit message for this diff:\n${diff}`,
  });

  const valid = /^(feat|fix|docs|chore)/.test(raw);

  if (!valid) {
    return await generateText({
      model: "gpt-4",
      prompt: `Fix this commit message to follow conventional commit format:\n${raw}`,
    });
  }

  return raw;
}

2. Routing

When to Use:

  • Tasks vary by type or domain
  • Specialized models or paths needed

Example: Classify and delegate user query

import { generateObject, generateText } from "ai";
import { z } from "zod";

const schema = z.object({
  type: z.enum(["code", "creative", "search"]),
  reason: z.string(),
});

const modelMap = {
  code: "claude",
  creative: "gpt-4",
  search: "gemini",
};

async function routeQuery(query) {
  const { type } = await generateObject({
    model: "gpt-4",
    schema,
    prompt: `Classify the query into code, creative, or search:\n${query}`,
  });

  return await generateText({ model: modelMap[type], prompt: query });
}

3. Parallelization

When to Use:

  • Multitasking subtasks simultaneously
  • Independent analysis by multiple agents

Example: Review code for security, performance, and maintainability

import { generateObject, generateText } from "ai";

async function reviewCode(code) {
  const tasks = [
    {
      label: "security",
      prompt: `List security issues in this code:\n${code}`,
    },
    { label: "performance", prompt: `Find performance bottlenecks:\n${code}` },
    {
      label: "maintainability",
      prompt: `Comment on maintainability:\n${code}`,
    },
  ];

  const reviews = await Promise.all(
    tasks.map((task) =>
      generateText({ model: "gpt-4", prompt: task.prompt }).then((result) => ({
        type: task.label,
        result,
      }))
    )
  );

  const summary = await generateText({
    model: "gpt-4",
    prompt: `Summarize the following review results:
${JSON.stringify(reviews)}`,
  });

  return summary;
}

4. Orchestrator

When to Use:

  • Controlled flow with dependency between steps
  • Multiple types of tasks need coordination

Example: Implementing a feature with task-specific calls

async function implementFeature(goal) {
  const plan = await generateObject({
    model: "openai/gpt-3.5",
    prompt: `Break down this feature into file tasks with change type:
${goal}`,
  });

  const fileChanges = await Promise.all(
    plan.files.map((file) => {
      const actionPrompt =
        file.changeType === "create"
          ? `Create a new file ${file.path} with purpose: ${file.purpose}`
          : file.changeType === "modify"
          ? `Improve existing file ${file.path}`
          : `Remove unused code in ${file.path}`;

      return generateText({ model: "gpt-4", prompt: actionPrompt });
    })
  );

  return fileChanges;
}

5. Evaluator + Optimizer

When to Use:

  • Iterative quality improvement
  • Evaluation-driven refinement

Example: Improve code comments until quality is acceptable

async function improveComment(code) {
  let comment = await generateText({
    model: "gpt-4",
    prompt: `Write a concise helpful comment for:
${code}`,
  });

  for (let attempt = 0; attempt < 3; attempt++) {
    const evalResult = await generateObject({
      model: "gpt-4",
      schema: z.object({ clarity: z.number(), issues: z.string() }),
      prompt: `Evaluate this comment: ${comment}`,
    });

    if (evalResult.clarity >= 8) break;

    comment = await generateText({
      model: "gpt-4",
      prompt: `Improve this comment based on issues:
${evalResult.issues}`,
    });
  }

  return comment;
}

Summary

| Workflow Type | Use Case | | --------------- | ------------------------------- | | Prompt Chaining | Structured, multi-step tasks | | Routing | Handle diverse task types | | Parallelization | Speed up subtasks | | Orchestrator | Fine-grain control, multi-agent | | Evaluator+Opt. | Iterative improvement |

Together, these patterns form the building blocks for robust, smart, autonomous agents. Agents are basically llms with added functionalities (tools calling) ˚ i wanna paste it to my medium articke

Jul 01, 2025

Blog Banner

React vs Next.js — What Actually Changes? A Beginner-Friendly Deep Dive

React is one of the most popular libraries for building modern web apps. If you’ve learned React, you’re already on the right track. But as your app grows, you may notice problems:

  • Slow initial load times
  • Poor SEO performance
  • Excessive boilerplate for data fetching
  • Growing JavaScript bundle sizes

Next.js 15 addresses these issues by combining the best of React with powerful features like server-side rendering, built-in routing, and more. Let’s explore how React and Next.js 15 differ, step by step.

What Happens When You Visit a React App?

When a user visits a React app (e.g., built with create-react-app or Vite), here’s what happens:

  1. Browser Sends a Request
    The browser requests the URL (e.g., GET https://your-react-app.com/). The server responds with a minimal HTML file:

    <html>
      <head>
        <title>Your App</title>
      </head>
      <body>
        <div id="root"></div>
        <script src="/main.js"></script>
      </body>
    </html>
    

    This HTML contains an empty <div id="root"> and a script tag for the JavaScript bundle.

  2. JavaScript Bundle Execution

    • The browser downloads the entire React app’s JavaScript code.
    • React “hydrates” the empty <div id="root">, rendering the app in the browser.
    • Data fetching (e.g., via useEffect or axios) happens after the initial render, causing re-renders once data is available.

Example: E-commerce Landing Page in React

Consider a landing page with a header, navigation, and a list of featured products fetched from an API:

import { useState, useEffect } from 'react';
import axios from 'axios';

function LandingPage() {
  const [products, setProducts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    axios.get('/api/featured-products')
      .then((res) => {
        setProducts(res.data);
        setLoading(false);
      });
  }, []);

  return (
    <div>
      <header>Shop Now!</header>
      <nav>Home | Products | Cart</nav>
      <section>
        <h1>Welcome to Our Store</h1>
        {loading ? (
          <p>Loading...</p>
        ) : (
          <div>
            <h2>Featured Products</h2>
            <ul>
              {products.map(product => (
                <li key={product.id}>{product.name}</li>
              ))}
            </ul>
          </div>
        )}
      </section>
    </div>
  );
}

Outcome:

  • The user sees a blank page or loading spinner until the JavaScript bundle downloads and the API call completes.
  • Search engines see only <div id="root">, harming SEO.
  • The entire app, including static components, is bundled into JavaScript, increasing load times.

Problems with Plain React (Client-Side Rendering)

  1. Poor SEO
    Search engine crawlers see an empty <div id="root"> with no meaningful content, leading to poor indexing and rankings.

  2. Slow First Load
    Users wait for the JavaScript bundle to download and execute before seeing content, resulting in a blank screen or spinner.

  3. Delayed Data Fetching
    Data is fetched client-side after the component mounts, causing additional renders and delays.

  4. Large JavaScript Bundles
    All components, even static ones, are included in the JavaScript bundle, increasing its size and slowing down the app.

How Next.js 15 Solves These Problems

Next.js 15 enhances React with features like server-side rendering (SSR), React Server Components, and the App Router. Here’s how it works:

Example: E-commerce Landing Page in Next.js 15

Using Next.js 15’s App Router and React Server Components, the same landing page is handled differently:

// app/page.tsx
import { db } from "@/lib/db";

export default async function LandingPage() {
  const products = await db.getFeaturedProducts(); // Fetches on the server

  return (
    <div>
      <header>Shop Now!</header>
      <nav>Home | Products | Cart</nav>
      <section>
        <h1>Welcome to Our Store</h1>
        <div>
          <h2>Featured Products</h2>
          <ul>
            {products.map(product => (
              <li key={product.id}>{product.name} - ${product.price}</li>
            ))}
          </ul>
        </div>
      </section>
    </div>
  );
}

What Happens:

  1. The Next.js server fetches the data (e.g., featured products) and renders the component into HTML.
  2. The browser receives a fully rendered HTML page, including product data, which displays instantly.
  3. Search engines can index the content, improving SEO.

Adding Interactivity: For client-side interactivity (e.g., an “Add to Cart” button), use a client component:

// app/components/AddToCartButton.tsx
'use client';
import { useState } from 'react';

export function AddToCartButton({ productId }) {
  const [added, setAdded] = useState(false);

  return (
    <button onClick={() => setAdded(!added)}>
      {added ? 'Added!' : 'Add to Cart'}
    </button>
  );
}

Integrate it into the server component:

// app/page.tsx (updated)
import { db } from "@/lib/db";
import { AddToCartButton } from "@/components/AddToCartButton";

export default async function LandingPage() {
  const products = await db.getFeaturedProducts();

  return (
    <div>
      <header>Shop Now!</header>
      <nav>Home | Products | Cart</nav>
      <section>
        <h1>Welcome to Our Store</h1>
        <div>
          <h2>Featured Products</h2>
          <ul>
            {products.map(product => (
              <li key={product.id}>
                {product.name} - ${product.price}
                <AddToCartButton productId={product.id} />
              </li>
            ))}
          </ul>
        </div>
      </section>
    </div>
  );
}

Outcome:

  • The page loads instantly with pre-rendered content.
  • Only interactive components (e.g., AddToCartButton) are included in the JavaScript bundle, reducing its size.
  • SEO is improved because the HTML contains all content.

Extra Features in Next.js 15

  1. Built-in API Routes
    Create backend endpoints without a separate server:

    // app/api/contact/route.ts
    export async function POST(req) {
      const data = await req.json();
      await db.save(data);
      return Response.json({ status: 'ok' });
    }
    
  2. Server Actions
    Handle form submissions without API routes:

    // app/actions.ts
    'use server';
    export async function handleSubmit(data) {
      await db.save(data);
    }
    
  3. App Router & File-Based Navigation
    Define routes using the file system:

    app/
    ├─ layout.tsx        # Shared layout
    ├─ page.tsx          # /
    ├─ about/
    │  └─ page.tsx       # /about
    ├─ posts/
    │  └─ page.tsx       # /posts
    ├─ api/
    │  └─ hello/
    │     └─ route.ts    # /api/hello
    

Summary: React vs Next.js 15

| Feature | React | Next.js 15 | |---------------------|---------------------------|--------------------------------| | Initial HTML | Blank <div> | Fully rendered content | | SEO | Poor | Excellent | | First Load | Slow | Fast | | Data Fetching | Client-side only | Server-side by default | | Routing | Manual (React Router) | File-based | | Bundle Size | Large | Smaller (server components) | | API Support | Needs backend | Built-in | | Server Logic | Not available | Server Actions supported |

Final Thoughts

React is a powerful frontend library, but it requires manual handling of routing, SEO, and performance. Next.js 15 builds on React, offering:

  • Better performance with server-side rendering
  • SEO out of the box
  • Simplified data fetching
  • Built-in backend capabilities
  • Smaller JavaScript bundles
  • Clean architecture with the App Router

If you know React, Next.js 15 is the logical next step to build fast, scalable, and SEO-friendly web apps.

Jun 05, 2025

Start Call