Beginner18 min

Learn how to use examples effectively to guide AI output with few-shot prompting techniques for better code generation.

Few-Shot Prompting

Few-shot prompting is one of the most powerful techniques for getting consistent, high-quality output from AI. By providing examples, you show the AI exactly what you want rather than just telling it.

What is Few-Shot Prompting?

Few-shot prompting provides demonstrations (examples) in the prompt to guide the model to better performance. It's called "few-shot" because you're giving a few examples (typically 2-5) before asking for the actual output.

ApproachDescriptionBest For
Zero-shotNo examples, just instructionsSimple, common tasks
One-shotSingle exampleFormat demonstration
Few-shot2-5 examplesComplex patterns, consistency

Why Few-Shot Works

OpenAI's research demonstrated that LLMs with large-scale parameters provide significantly better responses with few-shot prompts compared to zero-shot approaches. Examples help the model understand:

  • Format: Exactly how output should be structured
  • Style: The tone, verbosity, and conventions to follow
  • Edge cases: How to handle unusual inputs
  • Patterns: The underlying logic to apply

Basic Few-Shot Pattern

Terminal
Here are examples of the pattern I want:

Example 1:
Input: [input1]
Output: [output1]

Example 2:
Input: [input2]
Output: [output2]

Example 3:
Input: [input3]
Output: [output3]

Now apply this pattern:
Input: [actual input]

Few-Shot for Code Generation

Function Naming Convention

Terminal
Convert these function names to our camelCase convention:

Example 1:
Input: get_user_data
Output: getUserData

Example 2:
Input: FETCH_ALL_POSTS
Output: fetchAllPosts

Example 3:
Input: Send_Email_Notification
Output: sendEmailNotification

Now convert:
Input: calculate_total_price
Output: ???

Validation Functions

Generate TypeScript validation functions following this pattern:

Example 1 - Email Validation:

Terminal
export const validateEmail = (email: string): boolean => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
};

Example 2 - Phone Validation:

Terminal
export const validatePhone = (phone: string): boolean => {
  const phoneRegex = /^\+?[\d\s-]{10,}$/;
  return phoneRegex.test(phone.replace(/\s/g, ''));
};

Now generate: validateCreditCard

  • Should accept 13-19 digits
  • Allow spaces and dashes as separators
  • Use Luhn algorithm for checksum validation

Error Handling Pattern

Implement error handling following this pattern:

Example 1 - API Error:

Terminal
try {
  const response = await fetch(url);
  if (!response.ok) {
    throw new ApiError(`HTTP ${response.status}`, response.status);
  }
  return response.json();
} catch (error) {
  if (error instanceof ApiError) {
    logger.warn('API error', { status: error.status });
    throw error;
  }
  logger.error('Network error', { error });
  throw new NetworkError('Failed to connect');
}

Example 2 - Database Error:

Terminal
try {
  const result = await prisma.user.create({ data });
  return result;
} catch (error) {
  if (error instanceof Prisma.PrismaClientKnownRequestError) {
    if (error.code === 'P2002') {
      throw new DuplicateError('User already exists');
    }
  }
  logger.error('Database error', { error });
  throw new DatabaseError('Failed to create user');
}

Now implement error handling for: File upload to S3

Best Practices for Few-Shot

1. Use 3-5 Examples

Research shows 3-5 examples is optimal:

  • Fewer than 3: May not establish clear pattern
  • More than 5: Diminishing returns, wastes tokens

2. Make Examples Diverse

Cover different scenarios to prevent the AI from over-fitting to one pattern:

Terminal
Generate product descriptions:

Example 1 (Electronics):
Input: { name: "Wireless Mouse", price: 29.99, features: ["Bluetooth", "Ergonomic"] }
Output: "Stay productive with our Wireless Mouse ($29.99). Features Bluetooth connectivity and an ergonomic design for all-day comfort."

Example 2 (Clothing):
Input: { name: "Cotton T-Shirt", price: 19.99, features: ["100% Cotton", "Machine Washable"] }
Output: "Classic comfort meets style with our Cotton T-Shirt ($19.99). Made from 100% cotton and fully machine washable for easy care."

Example 3 (Food):
Input: { name: "Organic Coffee", price: 14.99, features: ["Fair Trade", "Medium Roast"] }
Output: "Start your day right with our Organic Coffee ($14.99). Fair Trade certified with a smooth medium roast profile."

Now generate for:
Input: { name: "Yoga Mat", price: 34.99, features: ["Non-Slip", "6mm Thick"] }

3. Include Edge Cases

Show how to handle unusual inputs:

Terminal
Format user names for display:

Example 1 (Normal):
Input: { firstName: "John", lastName: "Smith" }
Output: "John Smith"

Example 2 (Single name):
Input: { firstName: "Madonna", lastName: null }
Output: "Madonna"

Example 3 (Empty):
Input: { firstName: "", lastName: "" }
Output: "Anonymous User"

Example 4 (Special characters):
Input: { firstName: "José", lastName: "García-López" }
Output: "José García-López"

Now format:
Input: { firstName: null, lastName: "Chen" }

4. Use Clear Delimiters

Wrap examples in tags for clarity (especially with Claude):

Terminal
<examples>
  <example>
    <input>get_user_by_id</input>
    <output>getUserById</output>
  </example>
  <example>
    <input>FETCH_ORDERS</input>
    <output>fetchOrders</output>
  </example>
</examples>

Now apply this pattern to: calculate_shipping_cost

Few-Shot for Code Style Consistency

Establish your project's coding style:

Follow this code style for all generated functions:

Example - Data Transformation:

Terminal
/**
 * Transforms raw API user data into application format
 * @param rawUser - User data from API
 * @returns Normalized user object
 * @throws ValidationError if required fields missing
 */
export const normalizeUser = (rawUser: RawUser): User => {
  if (!rawUser.id || !rawUser.email) {
    throw new ValidationError('Missing required user fields');
  }

  return {
    id: rawUser.id,
    email: rawUser.email.toLowerCase().trim(),
    displayName: rawUser.name || 'Anonymous',
    createdAt: new Date(rawUser.created_at),
  };
};

Note the style:

  • JSDoc with @param, @returns, @throws
  • Input validation at the start
  • Explicit return type
  • Data normalization (lowercase, trim)
  • Sensible defaults

Now generate: normalizeProduct(rawProduct: RawProduct): Product

Few-Shot for Test Generation

Generate Jest tests following this pattern:

Example Test Suite:

Terminal
describe('validateEmail', () => {
  describe('valid emails', () => {
    it('should return true for standard email', () => {
      expect(validateEmail('user@example.com')).toBe(true);
    });

    it('should return true for email with subdomain', () => {
      expect(validateEmail('user@mail.example.com')).toBe(true);
    });
  });

  describe('invalid emails', () => {
    it('should return false for missing @', () => {
      expect(validateEmail('userexample.com')).toBe(false);
    });

    it('should return false for empty string', () => {
      expect(validateEmail('')).toBe(false);
    });
  });

  describe('edge cases', () => {
    it('should handle emails with plus signs', () => {
      expect(validateEmail('user+tag@example.com')).toBe(true);
    });
  });
});

Now generate tests for: validatePassword(password: string): boolean

  • Must be 8+ characters
  • Must contain uppercase
  • Must contain number
  • Must contain special character

Common Mistakes to Avoid

1. Examples Too Similar

Bad: All examples are happy path

Terminal
Example 1: valid email -> true
Example 2: another valid email -> true
Example 3: yet another valid email -> true

Good: Diverse scenarios

Terminal
Example 1: valid email -> true
Example 2: invalid format -> false
Example 3: empty string -> false
Example 4: edge case (plus sign) -> true

2. Inconsistent Format Across Examples

Bad: Different output structures

Terminal
Example 1 Output: { success: true, data: {...} }
Example 2 Output: { result: "ok", user: {...} }
Example 3 Output: "Success"

Good: Consistent structure

Terminal
Example 1 Output: { success: true, data: {...} }
Example 2 Output: { success: false, error: "..." }
Example 3 Output: { success: true, data: null }

3. Missing the Pattern

If your examples don't clearly show the pattern, the AI can't learn it:

Bad (What's the pattern?):

Terminal
Input: "hello" -> Output: "HELLO"
Input: "World" -> Output: "WORLD"

Good (Clear pattern):

Terminal
Convert to uppercase:
Input: "hello" -> Output: "HELLO"
Input: "World" -> Output: "WORLD"
Input: "TeSt" -> Output: "TEST"

When to Use Few-Shot

ScenarioRecommendation
Standard formatting (JSON, etc.)Zero-shot often sufficient
Custom output formatFew-shot recommended
Project-specific conventionsFew-shot essential
Complex transformationsFew-shot with edge cases
Consistent code styleFew-shot with style guide

Practice Exercise

Create a few-shot prompt for generating API error responses in your project's format.

Include examples for:

  1. Validation error (400)
  2. Not found error (404)
  3. Authentication error (401)
  4. Server error (500)

Summary

  • Few-shot prompting uses examples to demonstrate desired patterns
  • 3-5 diverse examples are optimal
  • Include edge cases to show complete behavior
  • Use consistent formatting across all examples
  • Wrap examples in tags for clarity

Next Steps

Now that you can use examples effectively, let's learn chain-of-thought prompting—a technique for getting AI to reason through complex problems step by step.

Mark this lesson as complete to track your progress