Post

GitHub Copilot | Part 2 - Advanced Prompt Engineering for Developer Happiness

Last week, I wrote a short post about my personal experience in using GitHub Copilot, including some tips for getting started.

This post is about having fun with GitHub Copilot by trying out long and complex prompts. After all, there is a clear link between developer productivity and developer happiness.

The prompts in this post may not be very useful for everyday work. Nevertheless, knowing how to engineer complex prompts will help in using GitHub Copilot more effectively. The goal is to gain the expertise to translate thoughts into working code as fast as possible (and have fun in the process!)

What is Prompt Engineering?

Prompt engineering is the process of structuring text that can be interpreted and understood by a generative AI model. A prompt is natural language text describing the task that an AI should perform (source: Wikipedia.)

In layman’s terms, a prompt is the message we send to the GenAI model and prompt engineering is the process of creating a message (a “prompt”) so that GenAI gives us the desired output.

Let’s begin.

Advanced Prompt Engineering with GitHub Copilot

Generate Java Classes and Mermaid Diagram

Azure OpenAI shares a very good article about advanced prompt engineering. Inspired by this article, I wrote this prompt.

Advanced GitHub Copilot Prompt: Generate Java Classes and Mermaid Diagram Example of an Advanced Prompt for GitHub Copilot Chat: Generate Java Classes and Mermaid Diagram (source here)

Think about this for a moment… The contents of the above multiline prompt are similar to the thoughts that are in our heads before writing code. Prompt engineering is simply articulating these thoughts into a “prompt” so that GenAI can convert the pseudo code in our heads into a target programming language code!

@GitHub Copilot:

Based on the requirements, we need to create two classes: Order and LineItem.

The Order class will have the following fields:

A list of LineItem objects A double for the total amount of the order A string for the status of the order The LineItem class will have the following fields:

An integer for the item ID A double for the item price An integer for the item quantity We also need to ensure that the list of LineItem objects in an Order has between 1 and 10 items, and that each LineItem has a quantity and price greater than 0.

Here is the Java code for these classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.util.List;

public class Order {
    private List<LineItem> lineItems;
    private double totalAmount;
    private String status;

    public Order(List<LineItem> lineItems, double totalAmount, String status) {
        if (lineItems.size() < 1 || lineItems.size() > 10) {
            throw new IllegalArgumentException("Order must have between 1 and 10 line items.");
        }
        this.lineItems = lineItems;
        this.totalAmount = totalAmount;
        this.status = status;
    }

    // getters and setters omitted for brevity
}

public class LineItem {
    private int itemId;
    private double itemPrice;
    private int itemQuantity;

    public LineItem(int itemId, double itemPrice, int itemQuantity) {
        if (itemQuantity <= 0 || itemPrice <= 0) {
            throw new IllegalArgumentException("Item quantity and price must be greater than 0.");
        }
        this.itemId = itemId;
        this.itemPrice = itemPrice;
        this.itemQuantity = itemQuantity;
    }

    // getters and setters omitted for brevity
}

Mermaid Markdown for class diagram:

1
2
3
4
5
6
7
8
9
10
11
12
classDiagram
    Order "1" -- "1..10" LineItem : contains
    class Order {
        +List<LineItem> lineItems
        +double totalAmount
        +String status
    }
    class LineItem {
        +int itemId
        +double itemPrice
        +int itemQuantity
    }

By placing the above Mermaid markdown in an MD file, we get the following visual class diagram:

classDiagram
    Order "1" -- "1..10" LineItem : contains
    class Order {
        +List<LineItem> lineItems
        +double totalAmount
        +String status
    }
    class LineItem {
        +int itemId
        +double itemPrice
        +int itemQuantity
    }

Fix the Mermaid Class Diagram and Generate Java Classes

The generated classes and mermaid diagram above look great, but not perfect. Using the Markdown Preview Enhanced extension, we can place the Mermaid diagram in an .md document and modify it in real time. After the improvements, we can ask GitHub Copilot to regenerate the Java classes.

Advanced GitHub Copilot Prompt: Generate Java Classes from Mermaid Diagram Generate Java Classes from Mermaid Diagram (md file here)

Now that looks better!

GitHub Copilot Chat does not “always” take the current file into context. When this happens, you can either

  • force it by using @workspace in the prompt (see here for more info.)
  • go to the text editor and Select All (CTRL+A), then open the Copilot Inline Chat (CTRL+I) and type the prompt there.

Generate a Java API and OpenAPI YAML Specification

GitHub Copilot Chat remembers the messages and responses in the conversation history. This means we can ask it to make revisions or generate additional code with follow-up messages like below:

Advanced GitHub Copilot Prompt: Follow Up Message Chat Follow Up Message/Prompt

@GitHub Copilot:

Sure, here’s a simple Java API using Spring Boot and the corresponding OpenAPI YAML specification.

Java API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api")
public class OrderController {

    @GetMapping("/orders")
    public List<Order> getAllOrders() {
        // implementation not shown
        return null;
    }

    @PostMapping("/orders")
    public Order createOrder(@RequestBody Order order) {
        // implementation not shown
        return null;
    }

    @GetMapping("/orders/{id}")
    public Order getOrderById(@PathVariable Long id) {
        // implementation not shown
        return null;
    }

    @PutMapping("/orders/{id}")
    public Order updateOrder(@PathVariable Long id, @RequestBody Order order) {
        // implementation not shown
        return null;
    }

    @DeleteMapping("/orders/{id}")
    public void deleteOrder(@PathVariable Long id) {
        // implementation not shown
    }
}

OpenAPI YAML specification:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
openapi: 3.0.0
info:
  title: Order API
  version: 1.0.0
paths:
  /orders:
    get:
      summary: Returns a list of orders
      responses:
        '200':
          description: A JSON array of order objects
          content:
            application/json:
              schema: 
                type: array
                items: 
                  $ref: '#/components/schemas/Order'
    post:
      summary: Creates a new order
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Order'
      responses:
        '201':
          description: Order created
  /orders/{id}:
    get:
      summary: Returns a specific order
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: A single order object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
    put:
      summary: Updates a specific order
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Order'
      responses:
        '200':
          description: Order updated
    delete:
      summary: Deletes a specific order
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: Order deleted
components:
  schemas:
    Order:
      type: object
      properties:
        id:
          type: integer
        status:
          type: string
        totalAmount:
          type: number
        lineItems:
          type: array
          items: 
            $ref: '#/components/schemas/LineItem'
    LineItem:
      type: object
      properties:
        itemId:
          type: integer
        lineItemPrice:
          type: number
        lineItemQuantity:
          type: integer

Please note that the actual implementation of the methods in the Java API is not shown. You would need to implement these methods according to your application’s business logic.

How cool is that? Makes you hope technical documentation are fully written using Markdown and Mermaid, right? 😎

Generate Terraform Infra-as-Code

Let’s change gears and make this more relevant to Infrastructure Teams. Code generation is not limited to application code. In this example, I tried generating a Terraform infra-as-code (IaC).

Advanced GitHub Copilot Prompt: Generate Terraform Infra-as-Code Generate Terraform Infra-as-Code (source here), using Copilot Inline Chat

Of course, we can also use GitHub Copilot to generate bash scripts, powershell scripts, and other types of Infra-as-Code (Azure Bicep, AWS CloudFormation, etc.)

Generate GitHub Actions YAML File

How about making this relevant to teams practicing DevOps? Let’s try generating a CI/CD pipeline using GitHub Actions.

Advanced GitHub Copilot Prompt: Generate GitHub Actions YAML Generate GitHub Actions YAML (source here), using Copilot Inline Chat

Let’s try adding a pipeline step to to ask for manual approval using ekeel/approval-action.

Advanced GitHub Copilot Prompt: Generate GitHub Actions YAML Add Step to GitHub Actions, using Copilot Inline Chat

Nice. The generated YAML file is not perfect, but it’s a good starting point for a pipeline engineer to work on.

Scaffolding a New Project

With the introduction of agents, we can now use GitHub Copilot to scaffold an entire VS Code workspace using @workspace /new. This removes the process of having to search for working starter codes online when starting a new project.

Advanced GitHub Copilot Prompt: Scaffold a new ReactTS Application Prompt: @workspace /new Scaffold a new chat ReactTS application that integrates with Azure OpenAI

Advanced GitHub Copilot Prompt: Scaffold a new ReactTS Application (Output) Scaffold Output

From this experiment, we see a basic ReactTS code structure generated. I did hope that it will also generate initial Chatbot and OpenAI SDK implementation code, but it didn’t. I also tried scaffolding with a more complex prompt that contains detailed instructions, but it still only generated minimal templated code… so maybe in the future? 🤭

I believe that the current intent of this new scaffolding feature (i.e. @workspace /new) is simply for generating the initial project file structure. Developers can then continue to use GitHub Copilot to work on the files individually. It is a copilot after all, not an autopilot.

Generating a New Jupyter Notebook

Now let’s try using the @workspace /newNotebook command to generate a a new Jupyter Notebook, something that AI/ML engineers and Python developers will find very useful.

Advanced GitHub Copilot Prompt: Generate a new Jupyter Notebook Generate a new Jupyter Notebook (source here)

In this experiment, GitHub Copilot picked up the Python libraries to use for calling Azure AI Speech Services and Azure Storage. I also liked that it generated Markdown sections before each Python code cell to make the code readable.

Please Share Your Prompts!

It is my hope that this fun exploration has inspired you to try advanced prompts of your own. Please do share your interesting prompts in the comments below. I am constantly looking for new ideas myself!

To see my other prompt explorations, check out this repo.

This post is licensed under CC BY 4.0 by the author.