GitHub Copilot integration guide cover image
AI/ML

GitHub Copilot Integration Guide

A production-ready training guide for Java, Spring Boot, Spring Batch, Servlets, JSP, MS-SQL, React, and Storybook workflows.

By aigrama
#github-copilot#ai#training

Purpose: Equip every developer in the organization with the skills to use GitHub Copilot effectively, responsibly, and efficiently across the full technology stack.


Table of Contents

  1. Introduction & Philosophy
  2. Architecture Overview
  3. Daily Developer Workflow
  4. Copilot in Java & Servlets/JSP
  5. Copilot in Spring Boot
  6. Copilot in Spring Batch
  7. Copilot with MS-SQL
  8. Copilot in React
  9. Copilot in Storybook
  10. Sample Project A — Spring Boot API
  11. Sample Project B — React + Storybook Component Library
  12. Prompt Patterns Reference
  13. Token-Saving Techniques
  14. AI Governance Policy
  15. Exercises & Evaluation Criteria

1. Introduction & Philosophy

GitHub Copilot is an AI pair programmer trained on billions of lines of public code. It integrates directly into IntelliJ IDEA Community and VS Code, the two IDEs approved for use in this organization. Copilot offers three interaction modes:

ModeTriggerBest For
Inline CompletionStart typingBoilerplate, method bodies, SQL
Copilot ChatCtrl+I / sidebarExplanation, refactoring, debugging
Slash Commands/explain, /fix, /testsTargeted single-file operations

Guiding Principles

  • You are the author. Copilot suggests; you decide. Review every suggestion before accepting.
  • Context is currency. The better your file, comments, and cursor position, the better the output.
  • Iterate, don’t regenerate. Refine a suggestion with follow-up prompts rather than dismissing and retrying.
  • Governance first. Never paste proprietary customer data, credentials, or PII into Copilot Chat.

Info: IntelliJ Community Edition requires the GitHub Copilot plugin (free tier available). VS Code requires the GitHub Copilot and GitHub Copilot Chat extensions.


2. Architecture Overview

2.1 How Copilot Processes Your Code

The diagram below shows how a developer’s keystrokes travel from the IDE through GitHub’s inference infrastructure and back as a suggestion.

2.2 Copilot in the Full-Stack Architecture


3. Daily Developer Workflow

A structured daily rhythm maximizes Copilot’s value without creating dependency or slowing review.

3.1 Workflow Checklist (Per Feature)

  • Read the ticket, understand acceptance criteria before opening Copilot
  • Write a plain-English comment above the method you want generated
  • Accept completions in small increments — use Ctrl+→ for partial acceptance
  • Run tests immediately after accepting a suggestion
  • Review generated code as if it came from a junior developer
  • Never commit code you cannot explain line-by-line

4. Copilot in Java & Servlets/JSP

4.1 Setting Up IntelliJ Community for Copilot

  1. Open File → Settings → Plugins → Marketplace
  2. Search for “GitHub Copilot” and install
  3. Restart IntelliJ, then Tools → GitHub Copilot → Login to GitHub
  4. Verify: open any .java file and start typing — ghost text should appear

4.2 Generating Servlet Boilerplate

Write this comment and let Copilot fill the method:

// Servlet that handles POST /api/orders, reads JSON body,
// validates non-null orderId and positive amount, then
// writes a 201 Created response with the saved order as JSON.
@WebServlet("/api/orders")
public class OrderServlet extends HttpServlet {

    // Copilot will suggest the full doPost body after you press Enter here
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // START TYPING: "String body = " → accept suggestion
    }
}

Prompt tip for Chat: "Refactor this Servlet to extract business logic into an OrderService class. Keep the Servlet thin."

4.3 JSP with JSTL — Copilot Suggestions

<%-- Copilot prompt comment: Generate a JSTL forEach loop that renders
     a Bootstrap 5 table of orders from the 'orderList' request attribute.
     Include columns: Order ID, Customer, Amount, Status, Created Date.
--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt" %>

<!-- Place cursor here and press Ctrl+I to open Copilot Chat inline -->
<table class="table table-striped">
  <thead>
    <tr>
      <th>Order ID</th><th>Customer</th>
      <th>Amount</th><th>Status</th><th>Created</th>
    </tr>
  </thead>
  <tbody>
    <%-- Copilot generates: --%>
    <c:forEach var="order" items="${orderList}">
      <tr>
        <td><c:out value="${order.orderId}"/></td>
        <td><c:out value="${order.customerName}"/></td>
        <td><fmt:formatNumber value="${order.amount}" type="currency"/></td>
        <td><span class="badge bg-${order.status == 'COMPLETED' ? 'success' : 'warning'}">
              <c:out value="${order.status}"/>
            </span></td>
        <td><fmt:formatDate value="${order.createdDate}" pattern="yyyy-MM-dd"/></td>
      </tr>
    </c:forEach>
  </tbody>
</table>

4.4 Useful Copilot Chat Prompts for Java/Servlet/JSP

GoalPrompt
Generate Servlet filter"Write a Servlet Filter that logs request method, URI, and response time in ms"
Explain legacy code/explain with the method selected
Convert Servlet → Spring MVC"Migrate this HttpServlet doGet to a Spring @GetMapping controller method"
Add JSP validation"Add client-side + server-side validation to this JSP form for email and phone fields"
Fix NPE/fix on the highlighted exception stack trace

5. Copilot in Spring Boot

5.1 Generating a REST Controller

/**
 * Copilot Chat prompt:
 * "Generate a Spring Boot REST controller for a Product entity with
 *  endpoints: GET /products, GET /products/{id}, POST /products,
 *  PUT /products/{id}, DELETE /products/{id}.
 *  Use ResponseEntity, include validation with @Valid,
 *  and inject ProductService via constructor injection."
 */
@RestController
@RequestMapping("/api/v1/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService productService;

    @GetMapping
    public ResponseEntity<List<ProductDto>> findAll(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        return ResponseEntity.ok(productService.findAll(PageRequest.of(page, size)));
    }

    @GetMapping("/{id}")
    public ResponseEntity<ProductDto> findById(@PathVariable Long id) {
        return productService.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<ProductDto> create(@Valid @RequestBody CreateProductRequest req) {
        ProductDto created = productService.create(req);
        URI location = URI.create("/api/v1/products/" + created.id());
        return ResponseEntity.created(location).body(created);
    }

    @PutMapping("/{id}")
    public ResponseEntity<ProductDto> update(
            @PathVariable Long id,
            @Valid @RequestBody UpdateProductRequest req) {
        return ResponseEntity.ok(productService.update(id, req));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        productService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

5.2 Generating Service + Repository Layer

// Copilot prompt above class: "Service layer for Product CRUD using JPA repository.
// Include pagination support and throw ResourceNotFoundException on missing records."

@Service
@RequiredArgsConstructor
@Transactional
public class ProductService {

    private final ProductRepository productRepository;
    private final ProductMapper mapper;

    @Transactional(readOnly = true)
    public List<ProductDto> findAll(Pageable pageable) {
        return productRepository.findAll(pageable)
                .stream()
                .map(mapper::toDto)
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public Optional<ProductDto> findById(Long id) {
        return productRepository.findById(id).map(mapper::toDto);
    }

    public ProductDto create(CreateProductRequest req) {
        Product entity = mapper.toEntity(req);
        return mapper.toDto(productRepository.save(entity));
    }

    public ProductDto update(Long id, UpdateProductRequest req) {
        Product entity = productRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Product", id));
        mapper.updateEntity(entity, req);
        return mapper.toDto(productRepository.save(entity));
    }

    public void delete(Long id) {
        if (!productRepository.existsById(id)) {
            throw new ResourceNotFoundException("Product", id);
        }
        productRepository.deleteById(id);
    }
}

5.3 application.yml Generation

# Copilot Chat: "Generate a Spring Boot application.yml for MS-SQL Server
# with connection pooling via HikariCP, JPA configured for MS-SQL dialect,
# separate profiles for dev and prod, and Actuator health endpoints exposed."
spring:
  application:
    name: product-api

  datasource:
    url: jdbc:sqlserver://${DB_HOST:localhost}:1433;databaseName=${DB_NAME:productdb};encrypt=true;trustServerCertificate=false
    username: ${DB_USER}
    password: ${DB_PASS}
    driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

  jpa:
    database-platform: org.hibernate.dialect.SQLServer2016Dialect
    hibernate:
      ddl-auto: validate
    show-sql: false
    properties:
      hibernate:
        format_sql: true
        jdbc:
          batch_size: 50

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,loggers
  endpoint:
    health:
      show-details: when-authorized

5.4 Exception Handling

// Copilot Chat: "Generate a @RestControllerAdvice for this Spring Boot app.
// Handle ResourceNotFoundException → 404, MethodArgumentNotValidException → 400
// with field error details, and generic Exception → 500.
// Return a standard ApiError record."

public record ApiError(int status, String message, Instant timestamp, List<String> errors) {
    public ApiError(int status, String message) {
        this(status, message, Instant.now(), List.of());
    }
}

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ApiError> handleNotFound(ResourceNotFoundException ex) {
        return ResponseEntity.status(404)
                .body(new ApiError(404, ex.getMessage()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiError> handleValidation(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult().getFieldErrors()
                .stream()
                .map(e -> e.getField() + ": " + e.getDefaultMessage())
                .collect(Collectors.toList());
        return ResponseEntity.status(400)
                .body(new ApiError(400, "Validation failed", Instant.now(), errors));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiError> handleGeneric(Exception ex) {
        return ResponseEntity.status(500)
                .body(new ApiError(500, "Internal server error"));
    }
}

6. Copilot in Spring Batch

Spring Batch has opinionated structure (Job → Step → Reader/Processor/Writer). Copilot is excellent at generating the plumbing once you describe the data flow.

6.1 Batch Architecture Flowchart

6.2 Generating a Batch Job

/**
 * Copilot Chat prompt:
 * "Generate a Spring Batch job that reads Order records from MS-SQL table 'dbo.orders'
 *  where status = 'PENDING', processes each by calling an external enrichment service,
 *  and writes the enriched records back to 'dbo.orders_processed'.
 *  Use chunk size 500, include skip logic for DataAccessException,
 *  and add a JobExecutionListener that logs start and completion times."
 */
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class OrderEnrichmentJobConfig {

    private final JobBuilderFactory jobs;
    private final StepBuilderFactory steps;
    private final DataSource dataSource;
    private final EnrichmentService enrichmentService;

    @Bean
    public Job orderEnrichmentJob(Step enrichStep, JobExecutionListener listener) {
        return jobs.get("orderEnrichmentJob")
                .incrementer(new RunIdIncrementer())
                .listener(listener)
                .flow(enrichStep)
                .end()
                .build();
    }

    @Bean
    public Step enrichStep() {
        return steps.get("enrichStep")
                .<PendingOrder, ProcessedOrder>chunk(500)
                .reader(pendingOrderReader())
                .processor(enrichmentProcessor())
                .writer(processedOrderWriter())
                .faultTolerant()
                .skipLimit(100)
                .skip(DataAccessException.class)
                .retryLimit(3)
                .retry(TransientDataAccessException.class)
                .listener(skipListener())
                .build();
    }

    @Bean
    @StepScope
    public JdbcPagingItemReader<PendingOrder> pendingOrderReader() {
        SqlPagingQueryProviderFactoryBean provider = new SqlPagingQueryProviderFactoryBean();
        provider.setDataSource(dataSource);
        provider.setSelectClause("SELECT order_id, customer_id, amount, created_date");
        provider.setFromClause("FROM dbo.orders");
        provider.setWhereClause("WHERE status = 'PENDING'");
        provider.setSortKeys(Map.of("order_id", Order.ASCENDING));

        JdbcPagingItemReader<PendingOrder> reader = new JdbcPagingItemReader<>();
        reader.setDataSource(dataSource);
        reader.setPageSize(500);
        reader.setRowMapper(new BeanPropertyRowMapper<>(PendingOrder.class));
        try {
            reader.setQueryProvider(provider.getObject());
        } catch (Exception e) {
            throw new BeanCreationException("Failed to create paging query provider", e);
        }
        return reader;
    }

    @Bean
    public ItemProcessor<PendingOrder, ProcessedOrder> enrichmentProcessor() {
        return order -> {
            EnrichmentResult result = enrichmentService.enrich(order);
            if (result == null) return null; // null = skip this item
            return ProcessedOrder.from(order, result);
        };
    }

    @Bean
    public JdbcBatchItemWriter<ProcessedOrder> processedOrderWriter() {
        return new JdbcBatchItemWriterBuilder<ProcessedOrder>()
                .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
                .sql("""
                    INSERT INTO dbo.orders_processed
                      (order_id, customer_id, amount, enriched_data, processed_at)
                    VALUES
                      (:orderId, :customerId, :amount, :enrichedData, GETDATE())
                    """)
                .dataSource(dataSource)
                .build();
    }

    @Bean
    public JobExecutionListener batchAuditListener() {
        return new JobExecutionListenerSupport() {
            @Override
            public void beforeJob(JobExecution je) {
                log.info("Job [{}] starting at {}", je.getJobInstance().getJobName(), Instant.now());
            }
            @Override
            public void afterJob(JobExecution je) {
                log.info("Job [{}] finished: {} | Duration: {}ms",
                        je.getJobInstance().getJobName(),
                        je.getStatus(),
                        je.getEndTime().getTime() - je.getStartTime().getTime());
            }
        };
    }
}

7. Copilot with MS-SQL

7.1 Generating Complex Queries

-- Copilot Chat prompt: "Write a T-SQL query that returns the top 10 customers
-- by total order value in the last 90 days, including their order count,
-- average order value, and last order date. Use CTEs for readability."

;WITH RecentOrders AS (
    SELECT
        customer_id,
        amount,
        created_date,
        ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_date DESC) AS rn
    FROM dbo.orders
    WHERE created_date >= DATEADD(DAY, -90, GETDATE())
      AND status NOT IN ('CANCELLED', 'REFUNDED')
),
CustomerMetrics AS (
    SELECT
        customer_id,
        SUM(amount)        AS total_value,
        COUNT(*)           AS order_count,
        AVG(amount)        AS avg_order_value,
        MAX(created_date)  AS last_order_date
    FROM RecentOrders
    GROUP BY customer_id
)
SELECT TOP 10
    cm.customer_id,
    c.full_name,
    c.email,
    cm.total_value,
    cm.order_count,
    cm.avg_order_value,
    cm.last_order_date
FROM CustomerMetrics cm
INNER JOIN dbo.customers c ON c.customer_id = cm.customer_id
ORDER BY cm.total_value DESC;

7.2 Generating Stored Procedures

-- Copilot Chat: "Write a stored procedure sp_UpsertProduct that accepts
-- product_id, name, price, stock_qty. If product_id exists, UPDATE;
-- if not, INSERT. Use output parameter @Result for 'INSERTED'/'UPDATED'.
-- Include error handling with TRY/CATCH and transaction."

CREATE OR ALTER PROCEDURE dbo.sp_UpsertProduct
    @ProductId   INT,
    @Name        NVARCHAR(200),
    @Price       DECIMAL(10,2),
    @StockQty    INT,
    @Result      NVARCHAR(10) OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRY
        BEGIN TRANSACTION;

        IF EXISTS (SELECT 1 FROM dbo.products WHERE product_id = @ProductId)
        BEGIN
            UPDATE dbo.products
            SET    name       = @Name,
                   price      = @Price,
                   stock_qty  = @StockQty,
                   updated_at = GETDATE()
            WHERE  product_id = @ProductId;
            SET @Result = 'UPDATED';
        END
        ELSE
        BEGIN
            INSERT INTO dbo.products (product_id, name, price, stock_qty, created_at)
            VALUES (@ProductId, @Name, @Price, @StockQty, GETDATE());
            SET @Result = 'INSERTED';
        END

        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
        THROW;
    END CATCH
END;
GO

7.3 Index Optimization Prompts

ScenarioCopilot Chat Prompt
Slow SELECT"Suggest indexes for this query. Explain the trade-off between read and write performance."
Fragmentation"Write T-SQL to check index fragmentation > 30% and rebuild; < 30% reorganize."
Query plan"Explain this execution plan XML. Identify the costliest operation."
Deadlock"I have this deadlock graph XML. What is the cause and how do I resolve it?"

8. Copilot in React

8.1 Component Generation Workflow

// Step 1: Write the interface first — Copilot uses it as context
interface OrderCardProps {
  orderId: string;
  customerName: string;
  amount: number;
  status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'CANCELLED';
  createdDate: string;
  onStatusChange?: (orderId: string, newStatus: string) => void;
}

// Step 2: Write the JSDoc comment — Copilot reads it for the implementation
/**
 * Displays a single order as a card with status badge and action button.
 * Status color: PENDING=amber, PROCESSING=blue, COMPLETED=green, CANCELLED=red.
 * Calls onStatusChange when the action button is clicked.
 */
// Step 3: Type "const OrderCard" and let Copilot complete the component
const OrderCard: React.FC<OrderCardProps> = ({
  orderId,
  customerName,
  amount,
  status,
  createdDate,
  onStatusChange,
}) => {
  const statusConfig = {
    PENDING:    { color: 'bg-amber-100 text-amber-800',   label: 'Pending'    },
    PROCESSING: { color: 'bg-blue-100 text-blue-800',     label: 'Processing' },
    COMPLETED:  { color: 'bg-green-100 text-green-800',   label: 'Completed'  },
    CANCELLED:  { color: 'bg-red-100 text-red-800',       label: 'Cancelled'  },
  };

  const { color, label } = statusConfig[status];

  const handleAction = () => {
    onStatusChange?.(orderId, status === 'PENDING' ? 'PROCESSING' : status);
  };

  return (
    <div className="border rounded-lg p-4 shadow-sm bg-white hover:shadow-md transition-shadow">
      <div className="flex justify-between items-start mb-3">
        <div>
          <p className="text-xs text-gray-500 font-mono">#{orderId}</p>
          <h3 className="font-semibold text-gray-900">{customerName}</h3>
        </div>
        <span className={`text-xs px-2 py-1 rounded-full font-medium ${color}`}>
          {label}
        </span>
      </div>
      <div className="flex justify-between items-center">
        <div>
          <p className="text-lg font-bold text-gray-900">
            {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount)}
          </p>
          <p className="text-xs text-gray-400">
            {new Date(createdDate).toLocaleDateString('en-US', { dateStyle: 'medium' })}
          </p>
        </div>
        {status === 'PENDING' && onStatusChange && (
          <button
            onClick={handleAction}
            className="text-sm px-3 py-1.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
          >
            Process
          </button>
        )}
      </div>
    </div>
  );
};

export default OrderCard;

8.2 Custom Hook Generation

// Copilot Chat: "Generate a custom React hook usePaginatedOrders that fetches
// orders from /api/v1/orders with page and size params, manages loading/error state,
// provides nextPage/prevPage functions, and uses AbortController for cleanup."

function usePaginatedOrders(initialSize = 20) {
  const [orders, setOrders]     = useState<Order[]>([]);
  const [page, setPage]         = useState(0);
  const [totalPages, setTotal]  = useState(0);
  const [loading, setLoading]   = useState(false);
  const [error, setError]       = useState<string | null>(null);

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);
    setError(null);

    fetch(`/api/v1/orders?page=${page}&size=${initialSize}`, {
      signal: controller.signal,
    })
      .then(res => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
      })
      .then(data => {
        setOrders(data.content);
        setTotal(data.totalPages);
      })
      .catch(err => {
        if (err.name !== 'AbortError') setError(err.message);
      })
      .finally(() => setLoading(false));

    return () => controller.abort();
  }, [page, initialSize]);

  return {
    orders,
    loading,
    error,
    page,
    totalPages,
    nextPage: () => setPage(p => Math.min(p + 1, totalPages - 1)),
    prevPage: () => setPage(p => Math.max(p - 1, 0)),
    hasPrev: page > 0,
    hasNext: page < totalPages - 1,
  };
}

9. Copilot in Storybook

9.1 Generating Stories with Args

// Copilot Chat: "Generate a Storybook 7 CSF3 story file for the OrderCard component.
// Include Default, Pending, Processing, Completed, Cancelled, and
// WithInteraction stories. Use @storybook/test for interaction testing."

import type { Meta, StoryObj } from '@storybook/react';
import { fn, expect, userEvent, within } from '@storybook/test';
import OrderCard from './OrderCard';

const meta: Meta<typeof OrderCard> = {
  title: 'Order/OrderCard',
  component: OrderCard,
  tags: ['autodocs'],
  argTypes: {
    status: {
      control: 'select',
      options: ['PENDING', 'PROCESSING', 'COMPLETED', 'CANCELLED'],
    },
    amount: { control: { type: 'number', min: 0, max: 99999, step: 0.01 } },
  },
  args: {
    onStatusChange: fn(),
  },
};
export default meta;
type Story = StoryObj<typeof meta>;

const baseArgs = {
  orderId: 'ORD-2024-00142',
  customerName: 'Acme Corporation',
  amount: 1_250.00,
  createdDate: '2024-11-01T10:30:00Z',
};

export const Pending: Story = {
  args: { ...baseArgs, status: 'PENDING' },
};

export const Processing: Story = {
  args: { ...baseArgs, status: 'PROCESSING' },
};

export const Completed: Story = {
  args: { ...baseArgs, status: 'COMPLETED' },
};

export const Cancelled: Story = {
  args: { ...baseArgs, status: 'CANCELLED' },
};

export const WithInteraction: Story = {
  args: { ...baseArgs, status: 'PENDING' },
  play: async ({ canvasElement, args }) => {
    const canvas = within(canvasElement);
    const button = await canvas.findByRole('button', { name: /process/i });
    await userEvent.click(button);
    expect(args.onStatusChange).toHaveBeenCalledWith('ORD-2024-00142', 'PROCESSING');
  },
};

9.2 Copilot Prompts for Storybook

TaskPrompt
Add a new story variant"Add a LargeAmount story with amount = 999999.99 and a LowAmount story with amount = 0.01"
Generate controls"Add argTypes for all boolean props as checkbox controls"
Add play function"Write a Storybook play function that tabs through all interactive elements and checks aria roles"
Generate docs page"Write a JSDoc description for this component suitable for Storybook autodocs"

10. Sample Project A — Spring Boot API

10.1 Project Structure

order-api/
├── src/
│   ├── main/
│   │   ├── java/com/company/orderapi/
│   │   │   ├── OrderApiApplication.java         ← @SpringBootApplication
│   │   │   ├── config/
│   │   │   │   ├── SecurityConfig.java          ← JWT + CORS
│   │   │   │   ├── SwaggerConfig.java           ← OpenAPI 3 docs
│   │   │   │   └── BatchConfig.java             ← Spring Batch beans
│   │   │   ├── controller/
│   │   │   │   ├── OrderController.java         ← REST endpoints
│   │   │   │   └── ProductController.java
│   │   │   ├── service/
│   │   │   │   ├── OrderService.java
│   │   │   │   └── ProductService.java
│   │   │   ├── repository/
│   │   │   │   ├── OrderRepository.java         ← JPA Repository
│   │   │   │   └── ProductRepository.java
│   │   │   ├── domain/
│   │   │   │   ├── entity/
│   │   │   │   │   ├── Order.java               ← @Entity, @Table("dbo.orders")
│   │   │   │   │   └── Product.java
│   │   │   │   └── dto/
│   │   │   │       ├── OrderDto.java            ← record
│   │   │   │       └── ProductDto.java
│   │   │   ├── batch/
│   │   │   │   ├── OrderEnrichmentJobConfig.java
│   │   │   │   ├── reader/PendingOrderReader.java
│   │   │   │   ├── processor/EnrichmentProcessor.java
│   │   │   │   └── writer/ProcessedOrderWriter.java
│   │   │   └── exception/
│   │   │       ├── ResourceNotFoundException.java
│   │   │       └── GlobalExceptionHandler.java
│   │   └── resources/
│   │       ├── application.yml
│   │       ├── application-dev.yml
│   │       ├── application-prod.yml
│   │       └── db/migration/                   ← Flyway scripts
│   │           ├── V1__create_orders.sql
│   │           └── V2__create_products.sql
│   └── test/
│       └── java/com/company/orderapi/
│           ├── controller/OrderControllerTest.java
│           ├── service/OrderServiceTest.java
│           └── batch/OrderEnrichmentJobTest.java
├── pom.xml
└── README.md

10.2 Key Dependencies (pom.xml excerpt)

<!-- Copilot Chat: "Generate pom.xml dependencies for Spring Boot 3.x with
     Spring Web, Spring Data JPA, Spring Batch, MS-SQL JDBC driver,
     Flyway, SpringDoc OpenAPI, Lombok, MapStruct, Spring Security,
     and Spring Boot Test." -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-batch</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>com.microsoft.sqlserver</groupId>
        <artifactId>mssql-jdbc</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.flywaydb</groupId>
        <artifactId>flyway-sqlserver</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
        <version>2.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.5.5.Final</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

10.3 Flyway Migration Script (MS-SQL)

-- V1__create_orders.sql
-- Copilot Chat: "Generate a Flyway V1 migration script for MS-SQL that creates
-- the dbo.orders table with UUID PK, customer_id FK, amount decimal,
-- status NVARCHAR with CHECK constraint, timestamps, and appropriate indexes."

CREATE TABLE dbo.customers (
    customer_id   BIGINT          NOT NULL IDENTITY(1,1) PRIMARY KEY,
    full_name     NVARCHAR(200)   NOT NULL,
    email         NVARCHAR(320)   NOT NULL UNIQUE,
    created_at    DATETIME2       NOT NULL DEFAULT GETDATE()
);

CREATE TABLE dbo.orders (
    order_id      BIGINT          NOT NULL IDENTITY(1,1) PRIMARY KEY,
    order_ref     NVARCHAR(36)    NOT NULL UNIQUE DEFAULT NEWID(),
    customer_id   BIGINT          NOT NULL,
    amount        DECIMAL(18,2)   NOT NULL,
    status        NVARCHAR(20)    NOT NULL DEFAULT 'PENDING'
                    CONSTRAINT CHK_orders_status
                    CHECK (status IN ('PENDING','PROCESSING','COMPLETED','CANCELLED','REFUNDED')),
    created_at    DATETIME2       NOT NULL DEFAULT GETDATE(),
    updated_at    DATETIME2       NOT NULL DEFAULT GETDATE(),
    CONSTRAINT FK_orders_customer
        FOREIGN KEY (customer_id) REFERENCES dbo.customers(customer_id)
);

CREATE INDEX IX_orders_status       ON dbo.orders (status)       INCLUDE (customer_id, amount);
CREATE INDEX IX_orders_customer     ON dbo.orders (customer_id)  INCLUDE (status, created_at);
CREATE INDEX IX_orders_created      ON dbo.orders (created_at DESC);

10.4 Integration Test with Copilot

// Copilot Chat: "Write a Spring Boot @SpringBootTest integration test for
// POST /api/v1/orders that inserts a test order and verifies the 201 response,
// Location header, and database record. Use TestRestTemplate and @Sql for setup."

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@Sql(scripts = "/test-data/orders-setup.sql", executionPhase = BEFORE_TEST_METHOD)
@Sql(scripts = "/test-data/orders-teardown.sql", executionPhase = AFTER_TEST_METHOD)
class OrderControllerIT {

    @Autowired private TestRestTemplate restTemplate;
    @Autowired private OrderRepository orderRepository;

    @Test
    void createOrder_returns201WithLocation() {
        var request = new CreateOrderRequest("CUST-001", new BigDecimal("299.99"));

        ResponseEntity<OrderDto> response = restTemplate.postForEntity(
                "/api/v1/orders", request, OrderDto.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
        assertThat(response.getHeaders().getLocation()).isNotNull();
        assertThat(response.getBody()).isNotNull();
        assertThat(response.getBody().amount()).isEqualByComparingTo("299.99");

        // Verify database persistence
        Optional<Order> saved = orderRepository.findByOrderRef(response.getBody().orderRef());
        assertThat(saved).isPresent();
        assertThat(saved.get().getStatus()).isEqualTo(OrderStatus.PENDING);
    }
}

11. Sample Project B — React + Storybook Component Library

11.1 Project Structure

order-ui/
├── src/
│   ├── components/
│   │   ├── OrderCard/
│   │   │   ├── OrderCard.tsx          ← Component
│   │   │   ├── OrderCard.stories.tsx  ← Storybook stories
│   │   │   ├── OrderCard.test.tsx     ← Vitest unit tests
│   │   │   ├── OrderCard.module.css   ← CSS Modules (optional)
│   │   │   └── index.ts               ← Barrel export
│   │   ├── OrderTable/
│   │   │   ├── OrderTable.tsx
│   │   │   ├── OrderTable.stories.tsx
│   │   │   └── index.ts
│   │   └── shared/
│   │       ├── Badge/
│   │       ├── Button/
│   │       └── Spinner/
│   ├── hooks/
│   │   ├── usePaginatedOrders.ts
│   │   └── useOrderMutation.ts
│   ├── pages/
│   │   ├── OrdersPage.tsx
│   │   └── OrderDetailPage.tsx
│   ├── services/
│   │   └── orderApi.ts               ← fetch wrappers
│   ├── types/
│   │   └── order.types.ts            ← shared TypeScript types
│   └── App.tsx
├── .storybook/
│   ├── main.ts                       ← Storybook config
│   └── preview.ts                    ← Global decorators, themes
├── package.json
└── vite.config.ts

11.2 TypeScript Types (Copilot-Generated)

// types/order.types.ts
// Copilot Chat: "Generate TypeScript interfaces for Order, Customer,
// and OrderStatus from the MS-SQL schema defined in V1__create_orders.sql"

export type OrderStatus =
  | 'PENDING'
  | 'PROCESSING'
  | 'COMPLETED'
  | 'CANCELLED'
  | 'REFUNDED';

export interface Customer {
  customerId: number;
  fullName: string;
  email: string;
  createdAt: string; // ISO 8601
}

export interface Order {
  orderId: number;
  orderRef: string; // UUID
  customerId: number;
  customer?: Customer;
  amount: number;
  status: OrderStatus;
  createdAt: string;
  updatedAt: string;
}

export interface PaginatedResponse<T> {
  content: T[];
  page: number;
  size: number;
  totalElements: number;
  totalPages: number;
  first: boolean;
  last: boolean;
}

export type CreateOrderRequest = Pick<Order, 'customerId' | 'amount'>;
export type UpdateOrderStatusRequest = { status: OrderStatus };

11.3 Storybook Configuration

// .storybook/main.ts
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-a11y',
    '@chromatic-com/storybook',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: { autodocs: 'tag' },
};
export default config;

12. Prompt Patterns Reference

These patterns are proven to produce high-quality Copilot completions and Chat responses.

12.1 The CRISP Pattern

Context → Role → Input → Style → Purpose

CONTEXT:   "I'm working on an order management API built with Spring Boot 3 and MS-SQL."
ROLE:      "Act as a senior Java architect."
INPUT:     "Here is the OrderService class: [paste code]"
STYLE:     "Use constructor injection, avoid field injection. Add Javadoc."
PURPOSE:   "Add optimistic locking to the update method to prevent concurrent modification."

Combined: “I’m working on a Spring Boot 3 / MS-SQL order API. Act as a senior Java architect. Here is my OrderService [paste]. Using constructor injection and Javadoc style, add optimistic locking to the update method to prevent concurrent modification.”

12.2 Pattern Library

PatternTemplateWhen to Use
Generate"Generate a [type] that [does X] using [technology]. Include [constraints]."Net new code
Refactor"Refactor this [class/method] to [goal]. Keep [constraint]. Do not change [boundary]."Improve existing
Explain/explain OR "Explain what this [code] does line by line. Identify any risks."Onboarding, review
Test"Write JUnit 5 tests for [class]. Cover happy path, null input, and [edge case]."TDD, coverage
Convert"Convert this [source pattern] to [target pattern]. Preserve the original behavior."Migration
Debug/fix OR "Here is the stack trace: [paste]. Explain the root cause and suggest a fix."Debugging
Optimize"Optimize this [SQL/method] for performance. Explain each change."Performance tuning
Document"Write Javadoc/JSDoc for this [class/function]. Include @param, @returns, @throws."Documentation

12.3 Anti-Patterns to Avoid

❌ "Write me a login system."                   → Too vague. No context, no constraints.
❌ "Fix my code." (no code provided)            → Copilot needs the actual code.
❌ "Is this code correct?"                      → Not actionable. Ask: "What are the risks?"
❌ Pasting 1,000+ lines into Chat              → Exceeds context; use /explain on a selection.
❌ Accepting suggestions without reading them  → Copilot can confidently generate wrong code.

13. Token-Saving Techniques

Copilot Chat has a context window limit. These techniques maximize signal-to-noise ratio.

13.1 Copilot Chat Context Hierarchy

13.2 Technique Reference

TechniqueSavingHow
Close unused filesHighCopilot reads all open tabs. Close unrelated files before Chat.
Use /explain on selectionHighSelect only the relevant 20–50 lines before using /explain.
New chat per featureHighStart a fresh chat at each feature boundary. Don’t carry long history.
Reference by name, not pasteMediumSay “in the OrderService.create() method” instead of pasting it.
Use slash commandsMedium/fix, /tests, /doc are shorter than equivalent natural language.
Summarize before continuingMediumAsk Copilot to “summarize the decisions made so far” and paste that summary into a new chat.
Write precise interfaces firstMediumA well-typed interface gives Copilot all the context it needs without prose.
Avoid repetition in promptsLowDon’t re-describe the tech stack in every message. Set it once at chat start.

13.3 Chat Initialization Template (paste at start of every session)

STACK: Java 21, Spring Boot 3.2, Spring Batch 5, MS-SQL 2019, React 18, Storybook 8
STYLE: Constructor injection, Records for DTOs, Functional React components, TypeScript strict
CONSTRAINTS: No Lombok on public APIs, use Optional not null returns, chunk size 500 for Batch
CURRENT TASK: [describe the feature you are working on]

14. AI Governance Policy

Warning: Mandatory Reading. All developers must read and acknowledge this section before using GitHub Copilot with organizational code.

14.1 What You May Do ✅

  • Use Copilot to generate boilerplate, unit tests, SQL queries, and documentation
  • Use Copilot Chat to explain, debug, and refactor code you own
  • Accept Copilot suggestions after reviewing them critically
  • Use Copilot for learning and exploration in sandbox/local environments
  • Generate data migration scripts (review before execution)

14.2 What You Must Never Do ❌

Prohibited ActionRiskEnforcement
Paste customer PII into ChatData privacy violationAutomatic audit log review
Paste API keys, passwords, secretsCredential exposureSecret scanning on commit
Accept suggestions without readingIntroducing vulnerabilitiesCode review checklist
Use Copilot to bypass security reviewsCompliance failurePR gate
Commit AI-generated code without testsQuality regressionCI test gate
Submit AI-generated code as your own without attributionIP riskPR template requires disclosure

14.3 Data Classification Rules

14.4 Code Review Requirements for AI-Generated Code

Every PR containing AI-assisted code must:

  1. Label the PR with the ai-assisted tag
  2. Pass all automated gates: unit tests, integration tests, static analysis (SonarQube), secret scanning
  3. Reviewer checklist:
    • I read every line of AI-generated code
    • I understand what each line does and can explain it
    • No hardcoded values that should be configurable
    • No unexplained magic numbers or SQL literals
    • Error handling is explicit, not implicit
    • No copyright or license markers in generated code
  4. Document the prompt used if the generated code is non-trivial (add to PR description)

14.5 Incident Response

If you accidentally paste sensitive data into Copilot Chat:

  1. Immediately close the Copilot Chat session
  2. Report to your team lead within 30 minutes
  3. Rotate any credentials that were exposed
  4. Document the incident in the security incident tracker
  5. GitHub does not store or train on Copilot for Business prompts, but treat it as potentially exposed

15. Exercises & Evaluation Criteria

15.1 Exercise Set A — Java & Spring Boot (Beginner)

Exercise A1: Servlet Refactoring

Take the provided LegacyOrderServlet.java (100+ line doGet method) and use Copilot to:

  • Extract business logic into an OrderService class
  • Add input validation
  • Write 3 unit tests

Evaluation Criteria:

CriterionPointsStandard
Service correctly extracted20No business logic remains in Servlet
Validation covers null + empty15Both null and empty string handled
Unit tests pass without mocking the Servlet15Pure service tests
Code compiles and runs10No syntax errors
Copilot prompts documented in PR10At least 2 prompts shown
Total70Pass: 49/70

Exercise A2: Spring Boot REST Endpoint

Generate a complete CRUD REST API for an Employee entity backed by MS-SQL. Include validation, pagination, and global error handling.

15.2 Exercise Set B — Spring Batch (Intermediate)

Exercise B1: CSV-to-Database Job

Create a Spring Batch job that:

  • Reads from a CSV file with 10,000 rows (provided)
  • Validates each row (required fields, numeric ranges)
  • Writes valid rows to dbo.employees, skips invalid rows
  • Produces a summary report at job completion

Evaluation Criteria:

CriterionPointsStandard
Job completes without exception20All 10k rows processed
Chunk size configured appropriately10Between 100–1000
Skip logic correctly implemented15Invalid rows logged, not thrown
Summary report generated15Shows processed/skipped counts
Integration test covers job execution15Job runs in test context
Token-saving techniques applied10New chat per component
Total85Pass: 60/85

15.3 Exercise Set C — React + Storybook (Intermediate)

Exercise C1: Data Table Component

Use Copilot to build an OrderTable component that:

  • Renders paginated orders from the API hook
  • Supports column sorting (click header)
  • Shows a loading skeleton while fetching
  • Has at least 4 Storybook stories (Default, Loading, Empty, Sorted)

Exercise C2: Storybook Interaction Test

Add a play function to one of your stories that:

  • Clicks a sortable column header
  • Verifies the sort icon changes direction
  • Verifies the data reorders

Evaluation Criteria:

CriterionPointsStandard
Component renders without errors15No console errors
Pagination works correctly15Next/prev, boundary behavior
Sort changes data order20Ascending and descending
Loading skeleton visible during fetch10At least 500ms simulated delay
4+ stories with meaningful args20Each story shows distinct state
Play function passes in Storybook test runner20storybook test passes
Total100Pass: 70/100

15.4 Exercise Set D — Governance (All Levels)

Exercise D1: Prompt Audit

Review the provided list of 10 Copilot Chat prompts. Classify each as:

  • ✅ Safe to use as-is
  • ⚠️ Safe with modification (explain what to change)
  • ❌ Prohibited (explain why)

Exercise D2: Token Optimization

Given a 10-message Copilot Chat history, identify which messages could be removed or summarized without losing essential context. Estimate token savings.


Appendix A: Quick Reference Card

Keyboard Shortcuts

ActionIntelliJVS Code
Accept suggestionTabTab
Reject suggestionEscEsc
Next suggestionAlt+]Alt+]
Prev suggestionAlt+[Alt+[
Partial accept (word)Ctrl+→Ctrl+→
Open Copilot ChatCtrl+ICtrl+I
Explain selection/explain in Chat/explain in Chat
Fix selection/fix in Chat/fix in Chat
Generate tests/tests in Chat/tests in Chat
  • IntelliJ Plugin: https://plugins.jetbrains.com/plugin/17718-github-copilot
  • VS Code Extension ID: GitHub.copilot and GitHub.copilot-chat
  • Copilot Docs: https://docs.github.com/en/copilot

Document version 1.0.0 · Platform Engineering · Last updated May 2026 Review cycle: Quarterly · Owner: Platform Engineering Lead

10.2 Starter: OrderApiApplication.java

@SpringBootApplication
@EnableScheduling
public class OrderApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApiApplication.class, args);
    }
}

10.3 Starter: Order Entity (MS-SQL mapped)

// Copilot Chat: "Generate a JPA entity for the dbo.orders table in MS-SQL.
// Fields: orderId (Long, PK, identity), customerId (Long), amount (BigDecimal),
// status (enum OrderStatus), createdDate (Instant), updatedDate (Instant).
// Add @CreatedDate and @LastModifiedDate with @EntityListeners(AuditingEntityListener.class)."

@Entity
@Table(name = "orders", schema = "dbo")
@EntityListeners(AuditingEntityListener.class)
@Getter @Setter
@NoArgsConstructor
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "order_id")
    private Long orderId;

    @Column(name = "customer_id", nullable = false)
    private Long customerId;

    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal amount;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private OrderStatus status = OrderStatus.PENDING;

    @CreatedDate
    @Column(name = "created_date", nullable = false, updatable = false)
    private Instant createdDate;

    @LastModifiedDate
    @Column(name = "updated_date")
    private Instant updatedDate;
}

10.4 Starter: Flyway Migration V1

-- src/main/resources/db/migration/V1__create_orders.sql
-- Copilot Chat: "Generate MS-SQL DDL for an orders table with all columns in the Order entity.
-- Add non-clustered index on customer_id and status."

CREATE TABLE dbo.orders (
    order_id      BIGINT         NOT NULL IDENTITY(1,1),
    customer_id   BIGINT         NOT NULL,
    amount        DECIMAL(10,2)  NOT NULL,
    status        NVARCHAR(20)   NOT NULL DEFAULT 'PENDING',
    created_date  DATETIME2(7)   NOT NULL DEFAULT SYSUTCDATETIME(),
    updated_date  DATETIME2(7)   NULL,
    CONSTRAINT PK_orders PRIMARY KEY CLUSTERED (order_id),
    CONSTRAINT CHK_orders_status CHECK (status IN ('PENDING','PROCESSING','COMPLETED','CANCELLED'))
);
GO

CREATE NONCLUSTERED INDEX IX_orders_customer_id ON dbo.orders (customer_id);
CREATE NONCLUSTERED INDEX IX_orders_status      ON dbo.orders (status) INCLUDE (customer_id, amount);
GO

10.5 Starter: Unit Test with Copilot

// Copilot Chat: "Write JUnit 5 unit tests for OrderService.create().
// Mock OrderRepository and OrderMapper. Test: happy path returns saved DTO,
// null request throws IllegalArgumentException."

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock  private OrderRepository  orderRepository;
    @Mock  private OrderMapper      mapper;
    @InjectMocks private OrderService sut;

    @Test
    void create_happyPath_returnsSavedDto() {
        // Arrange
        var req    = new CreateOrderRequest(1L, BigDecimal.TEN);
        var entity = new Order();
        var saved  = new Order(); saved.setOrderId(42L);
        var dto    = new OrderDto(42L, 1L, BigDecimal.TEN, OrderStatus.PENDING, Instant.now(), null);

        given(mapper.toEntity(req)).willReturn(entity);
        given(orderRepository.save(entity)).willReturn(saved);
        given(mapper.toDto(saved)).willReturn(dto);

        // Act
        OrderDto result = sut.create(req);

        // Assert
        assertThat(result.orderId()).isEqualTo(42L);
        then(orderRepository).should().save(entity);
    }

    @Test
    void create_nullRequest_throwsIllegalArgument() {
        assertThatThrownBy(() -> sut.create(null))
                .isInstanceOf(IllegalArgumentException.class);
    }
}

11. Sample Project B — React + Storybook Component Library

11.1 Project Structure

order-ui/
├── .storybook/
│   ├── main.ts           ← Vite builder, addons config
│   └── preview.ts        ← global decorators, Tailwind import
├── src/
│   ├── components/
│   │   ├── order/
│   │   │   ├── OrderCard/
│   │   │   │   ├── OrderCard.tsx          ← component (see § 8.1)
│   │   │   │   ├── OrderCard.stories.tsx  ← CSF3 stories (see § 9.1)
│   │   │   │   ├── OrderCard.test.tsx     ← Vitest + Testing Library
│   │   │   │   └── index.ts              ← re-export
│   │   │   └── OrderList/
│   │   │       ├── OrderList.tsx
│   │   │       ├── OrderList.stories.tsx
│   │   │       └── index.ts
│   │   └── common/
│   │       ├── Button/
│   │       ├── Badge/
│   │       └── Pagination/
│   ├── hooks/
│   │   ├── usePaginatedOrders.ts    ← custom hook (see § 8.2)
│   │   └── useOrderMutation.ts
│   ├── types/
│   │   └── order.types.ts           ← shared TypeScript interfaces
│   ├── utils/
│   │   ├── currency.ts
│   │   └── date.ts
│   └── App.tsx
├── package.json
├── tsconfig.json
├── vite.config.ts
└── tailwind.config.js

11.2 Storybook main.ts

// .storybook/main.ts
// Copilot Chat: "Configure Storybook 8 main.ts for a React + Vite + Tailwind project.
// Include @storybook/addon-essentials, @storybook/addon-interactions,
// @storybook/addon-a11y, and autodocs."

import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
  stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
  addons: [
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
    '@storybook/addon-a11y',
    '@storybook/addon-links',
  ],
  framework: {
    name: '@storybook/react-vite',
    options: {},
  },
  docs: {
    autodocs: 'tag',
  },
  typescript: {
    reactDocgen: 'react-docgen-typescript',
  },
};
export default config;

11.3 OrderList Component

// Copilot Chat: "Generate an OrderList component that uses usePaginatedOrders hook.
// Show a loading spinner while fetching, error message on failure,
// a responsive grid of OrderCard components, and Pagination controls at the bottom."

import { usePaginatedOrders } from '../../hooks/usePaginatedOrders';
import OrderCard from '../order/OrderCard';
import Pagination from '../common/Pagination';

const OrderList: React.FC = () => {
  const {
    orders, loading, error,
    page, totalPages, hasPrev, hasNext,
    nextPage, prevPage,
  } = usePaginatedOrders(12);

  if (loading) return (
    <div className="flex justify-center items-center h-64">
      <div className="w-10 h-10 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" />
    </div>
  );

  if (error) return (
    <div className="p-4 bg-red-50 border border-red-200 rounded-lg text-red-700">
      <strong>Error:</strong> {error}
    </div>
  );

  return (
    <div className="space-y-6">
      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
        {orders.map(order => (
          <OrderCard key={order.orderId} {...order} />
        ))}
      </div>
      {totalPages > 1 && (
        <Pagination
          currentPage={page}
          totalPages={totalPages}
          onPrev={prevPage}
          onNext={nextPage}
          hasPrev={hasPrev}
          hasNext={hasNext}
        />
      )}
    </div>
  );
};

export default OrderList;

11.4 Vitest Unit Test

// Copilot Chat: "Write Vitest + React Testing Library tests for OrderCard.
// Test: renders order ID, renders amount as currency, PENDING shows Process button,
// COMPLETED hides button, clicking Process calls onStatusChange."

import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import OrderCard from './OrderCard';

const base = {
  orderId: 'ORD-001',
  customerName: 'Test Corp',
  amount: 1250,
  status: 'PENDING' as const,
  createdDate: '2024-01-15T10:00:00Z',
};

describe('OrderCard', () => {
  it('renders order ID', () => {
    render(<OrderCard {...base} />);
    expect(screen.getByText(/#ORD-001/)).toBeInTheDocument();
  });

  it('formats amount as USD currency', () => {
    render(<OrderCard {...base} />);
    expect(screen.getByText('$1,250.00')).toBeInTheDocument();
  });

  it('shows Process button when PENDING', () => {
    render(<OrderCard {...base} />);
    expect(screen.getByRole('button', { name: /process/i })).toBeInTheDocument();
  });

  it('hides button when COMPLETED', () => {
    render(<OrderCard {...base} status="COMPLETED" />);
    expect(screen.queryByRole('button')).not.toBeInTheDocument();
  });

  it('calls onStatusChange with PROCESSING when Process clicked', () => {
    const onStatusChange = vi.fn();
    render(<OrderCard {...base} onStatusChange={onStatusChange} />);
    fireEvent.click(screen.getByRole('button', { name: /process/i }));
    expect(onStatusChange).toHaveBeenCalledWith('ORD-001', 'PROCESSING');
  });
});

12. Prompt Patterns Reference

Effective prompting is a learnable skill. These patterns are organized by intent and work across all languages in the stack.

12.1 The ROLE-CONTEXT-TASK-CONSTRAINT Pattern

"Act as a [ROLE] working on [CONTEXT].
 Your task is to [TASK].
 Constraints: [CONSTRAINT 1], [CONSTRAINT 2]."

Example:

"Act as a senior Spring Boot developer working on a high-traffic REST API.
 Your task is to refactor the OrderService.findAll() method to use database-level
 pagination instead of loading all records into memory.
 Constraints: keep the public method signature unchanged, use JPA Pageable,
 do not change the existing unit tests."

12.2 Pattern Catalogue

Pattern NameTemplateWhen to Use
Boilerplate Gen"Generate [artifact] for [entity] with [fields]. Use [framework/style]."Entities, DTOs, controllers
Explain It/explain or "Explain this code in plain English. Identify any anti-patterns."Legacy code, onboarding
Fix + Reason/fix or "Fix this bug. Explain the root cause before giving the fix."Debugging
Test First"Write failing unit tests for [method]. Do not implement the method."TDD workflow
Refactor"Refactor this to [pattern]. Keep behavior identical. Show diff."Code quality
Migrate"Migrate this [old tech] implementation to [new tech]. Preserve all logic."Modernization
Document"Write Javadoc/JSDoc for every public method in this file."Documentation debt
Security"Review this code for OWASP Top 10 vulnerabilities. List each risk."Security review
SQL Explain"Explain this SQL query in business terms. Suggest performance improvements."DBA collaboration
Batch Debug"This Spring Batch job fails at step 2. Trace the failure path through Reader→Processor→Writer."Batch troubleshooting

12.3 Multi-Turn Refinement Workflow

Turn 1: "Generate a Spring Boot service for Order CRUD."
Turn 2: "Add pagination to findAll(). Return Page<OrderDto>."
Turn 3: "Add a method findByCustomerId() with the same pagination."
Turn 4: "Now add @Cacheable on findById() with a 5-minute TTL."
Turn 5: "Write unit tests for all four methods."

This “progressive deepening” approach produces better results than one massive prompt and builds up the context window with agreed-upon code.


13. Token-Saving Techniques

Every Copilot Chat session has a context window limit. Burning tokens on irrelevant content degrades suggestion quality and risks truncation.

13.1 Context Window Flowchart

13.2 Token-Saving Rules

RuleDoDon’t
Scope precisely#file:OrderService.java@workspace for single-class questions
New chat per taskStart fresh session for unrelated featureContinue 50-message thread
Concise commentsOne-line intent comment above methodParagraph-length essay in file
Reference, don’t pasteReference the file pathPaste 500 lines into chat
Close unused tabsKeep ≤3 related files open20 open tabs polluting context
Use slash commands/fix, /explain, /testsFull natural-language re-description
Summarize long context"Summary: the above service handles..."Let conversation grow unchecked

13.3 IDE-Specific Tips

IntelliJ IDEA Community:

  • Use Alt+Enter → Ask Copilot on highlighted code — tight, focused context
  • Keep the Copilot tool window on a secondary monitor; close it when not in use
  • Prefer inline suggestions (ghost text) for known APIs; save Chat for logic

VS Code:

  • Use #selection instead of #file when only 20 lines are relevant
  • Pin the Copilot Chat tab in a split editor to avoid losing history
  • Install Copilot Labs extension for experimental features (summarize, brushes)

13.4 Writing Comments That Maximize Suggestion Quality

// ❌ Vague — Copilot produces generic boilerplate
// Get orders

// ✅ Specific — Copilot produces production-quality code
// Fetch orders for customerId where status IN (PENDING, PROCESSING),
// sorted by createdDate DESC, paginated. Throw ResourceNotFoundException
// if customerId does not exist in dbo.customers.
// ❌ Too vague
// Order card component

// ✅ Actionable
// Card showing orderId, customerName, formatted USD amount, status badge (color-coded),
// and a "Process" button visible only when status === 'PENDING'.
// Tailwind CSS. Accessible: button has aria-label. No external icon library.

14. AI Governance Policy

Warning: This section contains mandatory organizational policy. All developers must read and acknowledge this before using GitHub Copilot in any company repository.

14.1 Data Classification & Copilot Chat

Data TypeCopilot Chat Allowed?Action
Public documentation / specs✅ YesPaste freely
Internal architecture diagrams✅ YesDescribe in text
Generic code patterns (no real data)✅ YesPaste freely
Production database schemas (no data)⚠️ CautionAnonymize table/column names if sensitive
Customer PII (name, email, SSN, etc.)❌ NeverDo not paste under any circumstances
API keys, passwords, tokens❌ NeverUse environment variables; never paste
HIPAA / PCI-DSS protected data❌ NeverCompliance violation — reportable incident
Proprietary business logic (core IP)⚠️ CautionReview with tech lead before sharing

14.2 Code Review Requirements for AI-Generated Code

All code accepted from Copilot must pass the same review standards as human-authored code. Reviewers should additionally check:

  • Correctness: Does the logic handle edge cases (null, empty, overflow)?
  • Security: No SQL injection surfaces, no hardcoded secrets, no unsafe deserialization
  • Performance: No N+1 queries, appropriate pagination, no blocking I/O in reactive paths
  • Licensing: Copilot sometimes surfaces code matching public repositories. Flag any output that looks identical to a known library’s implementation.
  • Tests: AI-generated tests must have real assertions, not just assertNotNull(result)
  • Attribution: PRs that are >50% AI-generated should include a comment: // Generated with GitHub Copilot — reviewed by [Author]

14.3 Acceptable Use Policy

PERMITTED                                  PROHIBITED
─────────────────────────────────────────  ──────────────────────────────────────────
✅ Generating boilerplate code             ❌ Bypassing code review via AI-generation
✅ Writing unit/integration tests          ❌ Pasting production data into Chat
✅ Explaining legacy code                  ❌ Using Copilot to circumvent security review
✅ Suggesting SQL query optimizations      ❌ Auto-committing suggestions without review
✅ Drafting technical documentation        ❌ Using on classified/restricted projects
✅ Refactoring for readability             ❌ Sharing output externally without review
✅ Debugging with /fix                     ❌ Relying solely on AI for security analysis

14.4 Incident Reporting

If you suspect you have accidentally submitted PII or credentials to Copilot Chat:

  1. Do not panic. GitHub does not store Copilot Chat messages after the session ends (per GitHub’s data handling policy — verify with your GitHub Enterprise admin for org-specific settings).
  2. Document what was submitted — data type, approximate volume, timestamp.
  3. Report within 2 hours to your team lead and the Security team via the internal incident portal.
  4. Follow the Data Breach Response Checklist in the security wiki.

14.5 Copilot Settings — Required Organization Configuration

Your GitHub organization admin should enforce these settings in GitHub Enterprise → Copilot Policy:

# Required organizational Copilot settings
copilot:
  suggestions:
    public_code_matching: blocked        # Block suggestions matching public repo code
  chat:
    web_search: disabled                 # Disable web search in Chat (data privacy)
  seat_management:
    assignment: selected_teams_only      # Limit to approved development teams
  audit_log:
    enabled: true                        # All usage logged for compliance

14.6 Governance Flowchart


15. Exercises & Evaluation Criteria

15.1 Exercise Set A — Java & Spring Boot (Beginner)

Exercise A1: Servlet Migration

Using Copilot, migrate the provided CustomerServlet.java (doGet + doPost) into a Spring Boot @RestController. The controller must preserve all original endpoints, add input validation with @Valid, and return proper HTTP status codes.

Evaluation Criteria:

CriterionPoints
All original endpoints present and functional20
Input validation with field-level error messages15
Correct HTTP status codes (200/201/400/404/500)15
Constructor injection used (no @Autowired field injection)10
At least 3 Copilot Chat prompts documented in PR description10
Unit tests with ≥80% method coverage20
Code passes reviewer AI checklist (§14.2)10
Total100

Exercise A2: Spring Batch Job

Create a Spring Batch job that reads from dbo.customers (status = ‘INACTIVE’), calls a stub ReactivationService.evaluate() on each record, and writes ELIGIBLE records to dbo.reactivation_candidates. Include skip/retry and a JobExecutionListener that logs summary stats.

Evaluation Criteria:

CriterionPoints
Job completes without error on 10,000-row test dataset25
Chunk size configured appropriately (≥100)10
Skip and retry policies implemented and tested15
Null return from processor correctly skips the item10
Listener logs: job name, status, items read/written/skipped15
Integration test using @SpringBatchTest15
Copilot prompts documented (min 5 prompts used)10
Total100

15.2 Exercise Set B — React & Storybook (Intermediate)

Exercise B1: Data Table Component

Build a CustomerTable component in React + TypeScript that:

  • Displays customers with sortable columns (name, email, status, created date)
  • Supports client-side filtering by name
  • Uses the provided useCustomers hook
  • Has full Storybook coverage (4+ stories including an interaction test)
  • Is fully keyboard accessible

Evaluation Criteria:

CriterionPoints
All columns render correctly with proper data types15
Sorting works for all 4 columns (asc/desc toggle)20
Filter updates table in real time10
4+ Storybook stories including WithInteraction20
Zero axe accessibility violations in Storybook a11y panel15
Vitest tests ≥75% coverage10
Copilot prompts documented (min 5)10
Total100

15.3 Exercise Set C — MS-SQL & Integration (Advanced)

Exercise C1: Query Optimization

The following query takes 8+ seconds on a 5M-row table. Use Copilot to:

  1. Understand the query’s intent
  2. Identify the performance bottleneck
  3. Propose and implement index changes
  4. Rewrite using CTEs for readability
  5. Document the change in a migration script

Evaluation Criteria:

CriterionPoints
Correct identification of bottleneck (e.g., missing index, full table scan)20
New query executes in <1 second on test dataset20
At least one new index with INCLUDE columns15
CTE rewrite is semantically equivalent15
Flyway migration script follows naming convention10
Explain plan before/after documented in PR10
Copilot prompts used and documented10
Total100

15.4 Copilot Proficiency Self-Assessment

Rate yourself after completing the exercises:

Skill AreaNovice (1)Developing (2)Proficient (3)Expert (4)
Writing effective promptsVague, one-linersContext includedROLE-CONTEXT-TASK-CONSTRAINT patternMulti-turn refinement, consistent output
Reviewing AI suggestionsAccept allCheck syntaxCheck logic + edge casesFull security + performance review
Token management@workspace alwaysFiles open as needed#file referencesNew chat per context, comments optimized
Governance complianceUnawareKnows rulesApplies rules consistentlyCan advise teammates
Test generationNo testsTests existTests have real assertionsTDD: tests before implementation

Target: All developers at Proficient (3) within 30 days. Expert (4) for tech leads.


Appendix A — Quick Reference Card

┌──────────────────────────────────────────────────────────────┐
│           GITHUB COPILOT — DAILY QUICK REFERENCE             │
├──────────────────┬───────────────────────────────────────────┤
│ INLINE           │ Tab = accept  |  Esc = reject             │
│                  │ Ctrl+→ = accept word  |  Alt+[ / ] = cycle│
├──────────────────┼───────────────────────────────────────────┤
│ CHAT TRIGGERS    │ Ctrl+I = inline chat                      │
│                  │ Ctrl+Shift+I = sidebar chat               │
├──────────────────┼───────────────────────────────────────────┤
│ SLASH COMMANDS   │ /explain  /fix  /tests  /doc  /simplify   │
├──────────────────┼───────────────────────────────────────────┤
│ CONTEXT REFS     │ #file:Foo.java  #selection  #codebase     │
├──────────────────┼───────────────────────────────────────────┤
│ TOKEN SAVING     │ New chat per task | Close unused tabs      │
│                  │ Use #file not @workspace | Brief comments  │
├──────────────────┼───────────────────────────────────────────┤
│ NEVER PASTE      │ PII | Passwords | Customer data | Secrets  │
├──────────────────┼───────────────────────────────────────────┤
│ ALWAYS REVIEW    │ Logic | Edge cases | Security | Tests      │
└──────────────────┴───────────────────────────────────────────┘
ResourceURLNotes
GitHub Copilot Docsdocs.github.com/copilotOfficial, always current
Copilot for Java (JetBrains)plugins.jetbrains.com/plugin/17718IntelliJ plugin page
Spring Boot Referencedocs.spring.io/spring-bootCopilot loves official docs as context
Storybook Docsstorybook.js.org/docsCSF3 format reference
OWASP Top 10owasp.org/Top10Use for security review checklist
GitHub Copilot Trust Centerresources.github.com/copilot-trust-centerData handling, privacy