Skip to main content

Lab 2: Prompt Engineering Challenge

🎯 Learning Objectives
  • Master five prompting techniques: zero-shot, few-shot, comment-driven, context crafting, and iterative refinement
  • Experience the difference between vague and specific prompts firsthand
  • Apply the 4S Framework (Single, Specific, Short, Surround) to every prompt

The 4S Framework (Reference)

  • 🎯 Single — One task per prompt. Break complex goals into discrete steps.
  • 🔍 Specific — Name technologies, specify types, mention edge cases.
  • ✂️ Short — Concise beats verbose. Get to the point.
  • 🔄 Surround — Provide context: open relevant files, add type annotations, use meaningful names.

Exercises

Step 1

Zero-Shot Prompting — Luhn Algorithm

Zero-shot means giving the model a task with no examples — just a clear description of what you want.

Part A: The Vague Prompt (Anti-Pattern)

  1. Create a new file: lab2-prompts.ts
  2. Open Copilot Chat and type this deliberately vague prompt:
    write a credit card validator
  3. Record what you get. Note:
    • Does it specify the algorithm?
    • Does it handle edge cases (empty string, non-numeric input, wrong length)?
    • Does it include types?
    • What language did it choose?

Part B: The 4S Prompt (Best Practice)

  1. Now rewrite the prompt using the 4S Framework:
    Write a TypeScript function called validateCreditCard that takes a 
    string of digits and returns a boolean. Use the Luhn algorithm. 
    Handle edge cases: empty string, non-numeric characters, and 
    strings shorter than 13 or longer than 19 digits. Include JSDoc.
  2. Compare the two results side-by-side:
❌ Vague Prompt

Ambiguous language, no type info, no edge cases specified, model guesses everything.

✅ 4S Prompt

Single task, specific algorithm + types + edge cases, short and direct, language context provided.

💡 Key Insight: The quality of Copilot output is directly proportional to the quality of your prompt. A 30-second investment in writing a clear prompt saves minutes of fixing bad output.
Step 2

Few-Shot Prompting — Date Format Conversion

Few-shot prompting gives the model examples of input → output to establish a pattern. The model then follows the pattern for new inputs.

Instructions

  1. In lab2-prompts.ts, type the following pattern of examples. Type them manually — don't paste — so Copilot sees you building the pattern:
    // Date format conversions: US → ISO
    // "01/15/2025" → "2025-01-15"
    // "12/31/2024" → "2024-12-31"  
    // "03/07/2026" → "2026-03-07"
    //
    // Now convert: "06/19/2026" →
  2. Copilot should auto-complete with "2026-06-19" — demonstrating it learned the pattern.
  3. Now below that, write a function using the pattern:
    // Convert US date (MM/DD/YYYY) to ISO format (YYYY-MM-DD)
    // Examples:
    // convertDate("01/15/2025") → "2025-01-15"
    // convertDate("12/31/2024") → "2024-12-31"
    function convertDate(usDate: string): string {
  4. Let Copilot generate the implementation. Verify it follows the pattern correctly.
  5. Test it mentally: does the generated code handle invalid dates? Single-digit months/days?
💡 Exam Tip: The GH-300 exam will ask: "Which prompting technique provides multiple input/output examples to establish a pattern?" Answer: Few-shot prompting.
Step 3

Comment-Driven Development — Binary Search Tree

Write a complete specification as comments/JSDoc first, then let Copilot implement from your spec. This is the recommended best practice for getting high-quality suggestions.

Instructions

  1. Create a new file: lab2-bst.ts
  2. Write the following JSDoc specification (type it, don't paste):
    /**
     * Binary Search Tree implementation
     * 
     * @class BinarySearchTree
     * @template T - The type of values stored in the tree
     * 
     * Methods:
     * - insert(value: T): void — Insert a value maintaining BST property
     * - search(value: T): boolean — Return true if value exists
     * - inOrderTraversal(): T[] — Return sorted array of all values
     * - delete(value: T): void — Remove value, maintaining BST property
     * - min(): T | undefined — Return minimum value
     * - max(): T | undefined — Return maximum value
     * 
     * Properties:
     * - root: TreeNode<T> | null
     * - size: number (readonly)
     * 
     * Edge cases:
     * - Duplicate values should be ignored (no duplicates)
     * - Operations on empty tree should not throw
     */
  3. Press Enter after the closing */ and type:
    interface TreeNode<T> {
  4. Let Copilot generate the interface. It should create value, left, and right fields based on your spec.
  5. Then type:
    class BinarySearchTree<T> {
  6. Let Copilot generate the entire class. Because your JSDoc was thorough, the implementation should include all specified methods.
  7. Review the generated code:
    • Does it implement all 6 methods from your spec?
    • Does it handle duplicates as specified?
    • Does it handle empty tree operations?
💡 Best Practice: Writing detailed comments/JSDoc before code is the single most impactful thing you can do for Copilot suggestion quality. The exam calls this "comment-driven development."
Step 4

Context Crafting — Repository Pattern from Types

Context crafting means opening relevant files in your editor so Copilot can see types, interfaces, and patterns to follow.

Instructions

  1. Create a type definitions file: lab2-types.ts
    export interface User {
      id: string;
      name: string;
      email: string;
      role: 'admin' | 'editor' | 'viewer';
      createdAt: Date;
      updatedAt: Date;
    }
    

    export interface CreateUserInput {
    name: string;
    email: string;
    role: ‘admin’ | ’editor’ | ‘viewer’;
    }

    export interface UpdateUserInput {
    name?: string;
    email?: string;
    role?: ‘admin’ | ’editor’ | ‘viewer’;
    }

    export interface UserRepository {
    findById(id: string): Promise<User | null>;
    findByEmail(email: string): Promise<User | null>;
    findAll(filter?: { role?: string }): Promise<User[]>;
    create(input: CreateUserInput): Promise<User>;
    update(id: string, input: UpdateUserInput): Promise<User>;
    delete(id: string): Promise<void>;
    }



  2. Keep this file open in a tab. This is the key — Copilot uses open files as context.

  3. Create a new file: lab2-user-repo.ts

  4. Type at the top:

    import { User, CreateUserInput, UpdateUserInput, UserRepository } from './lab2-types';
    
    // In-memory implementation of UserRepository for testing
    </li>
    <li>Then type:
    
    export class InMemoryUserRepository implements UserRepository {
    </li>
    <li>Let Copilot generate the implementation. Because the type file is open, Copilot knows exactly what methods to implement and their signatures.</li>
    <li>Verify that every method from the <code>UserRepository</code> interface is implemented correctly.</li>
    
💡 Context Crafting Rule: Copilot can "see" (1) the current file, (2) other open tabs, and (3) files referenced via imports. Keep relevant type definitions, interfaces, and example files open to dramatically improve suggestion quality.
Step 5

Iterative Refinement — Sort, Validate, Optimize

Real-world Copilot usage is iterative: generate, review, refine, improve. This exercise simulates that workflow.

Instructions

  1. Open Copilot Chat. Start with a basic request:
    Write a TypeScript function called smartSort that sorts an array of 
    numbers. Use quicksort algorithm.
  2. Review the output. Insert it into a new file lab2-sort.ts.
  3. Now iterate. In Chat, type:
    Add input validation: handle empty arrays, single-element arrays, 
    and arrays with non-finite numbers (NaN, Infinity). Throw a 
    descriptive TypeError for invalid inputs.
  4. Apply the updated version. Then iterate again:
    Optimize for large arrays (100k+ elements): add insertion sort 
    fallback for partitions smaller than 10 elements. Add a comparison 
    counter parameter for benchmarking.
  5. Apply the optimized version. One more iteration:
    Add comprehensive JSDoc with @param, @returns, @throws, @example 
    tags. Include time and space complexity analysis in the docs.
  6. Compare your final version to the first version. The iterative approach produced code that is validated, optimized, and documented — far better than any single prompt could achieve.
💡 Key Insight: Iterative refinement is the most valuable real-world prompting skill. Don't try to get everything in one prompt. Generate → Review → Refine → Repeat.
Step 6

Bonus: Create a Custom Instructions File

Lock in your team's standards so every Copilot interaction follows your conventions automatically.

Instructions

  1. Create the file .github/copilot-instructions.md in your project root:
    # Copilot Instructions
    
    

    Language & Style
    #

    • Use TypeScript strict mode for all code
    • Prefer const over let; never use var
    • Use async/await, never raw Promises or callbacks

    Documentation
    #

    • All exported functions must have JSDoc with @param and @returns
    • Include @example for public API functions

    Error Handling
    #

    • Use custom error classes extending Error
    • Never catch and silently ignore errors
  2. Save the file. Now go back to Chat and ask:
  3. Write a function that fetches user data from an API endpoint
    </li>
    <li>Verify that Copilot follows your instructions: TypeScript, const, async/await, JSDoc, proper error handling.</li>
    <li>Try violating one rule — ask for callback-style code. Copilot should still prefer async/await if your instructions are active.</li>
    
💡 Exam Tip: The exam asks: "What file configures repository-level Copilot instructions?" Answer: .github/copilot-instructions.md. It applies to all contributors who use Copilot in that repository.

✅ Completion Checklist

  • Compared a vague prompt vs. a 4S-framework prompt — saw the quality difference
  • Used few-shot prompting to teach Copilot a date format pattern
  • Wrote JSDoc spec first, then let Copilot implement a full BST class
  • Used context crafting (open type file) to generate a repository implementation
  • Iteratively refined a sort function through 4 rounds of improvement
  • Created a .github/copilot-instructions.md and verified Copilot follows it

🎯 Key Takeaways for the Exam

  • 4S Framework: Single, Specific, Short, Surround
  • Zero-shot: No examples, just a clear task description
  • Few-shot: Provide input/output examples to establish a pattern
  • Comment-driven: Write specs first, let Copilot implement
  • Context references: #project = entire project, #file = specific file, #selection = selected code
  • .github/copilot-instructions.md applies repo-wide custom instructions