7 minute read

A man typing away on a keyboard working on some kind of HTML code
Generated by Gen AI from Microsoft Copilot using DALL.E

Introduction

Github Copilot recently introduced the ability to edit multiple files in VS code. As of writing, this feature is currently in preview and titled Edit with Copilot

The possibilities with this capability are immense. You can now pair more efficiently with Copilot and ask it to make changes in your codebase across multiple files in natural language

I took this feature for a spin on the test-infra codebase with a straightforward use case and found the results surprisingly accurate.

In this blog, Let’s take a look at an example workflow step by step and understand how to work with this feature and then also discuss some ideas on how this could be useful in your day-to-day workflows

I use Jetbrains IntelliJ IDEA IDE for most of my professional development, but this capability is not exposed yet on the GitHub plugin there. We’ll use VS code to see this workflow. If you are not familiar with VS code yet you can read this to understand some of the basics

Without further adieu let’s dive in 🏊

Launch Edit with Copilot

When you start Copilot edits in VS code you are greeted with this UI. You can add files to your working set. In this case, I have a test file opened which was added by default and we can also add additional files

Another useful feature is that you can switch the model that would be used for code generation under the hood such as Claude 3.5 Sonnet from Anthropic or GPT 4o from Open AI

Launch edit with Copilot

Describe the change

Copilot edit is more like an experienced pair engineer in the driver role while you adopt the navigator/observer/reviewer role and instruct to perform some edits.

To begin with, You should have a clear idea of what change you want to make in the codebase. Quite often this is the most challenging aspect of working on large codebases because it implies you have a solid understanding of the code structure and whats happening under the hood.

If you are not sure where the changes need to be made, the results probably won’t be great.

In such cases using chat to figure out higher-level solutions or brainstorming before you jump into edits could be a good way of thinking about this.

Let’s say I have the below test method ReqResCreateUserTest written in Java and I want to make a simple change to introduce a BaseTest class and move the setup method to that. There are other test classes in the same package that have this duplicated code that I would want to apply this refactoring later on

package io.automationhacks.testinfra.reqres.users;



@OnCall(DISHA)
@Flow(Flows.USERS)
public class ReqResCreateUserTest {

    @BeforeClass(alwaysRun = true)
    public void setup() {
        RestAssured.baseURI = "https://reqres.in/api";
    }

    @Test(groups = {Team.IDENTITY, Groups.SMOKE})
    @Service(Services.CREATE_USER)
    @Attributes(attributes = {@Attribute(key = "team", value = "identity")})
    public void testCreate() {
        String requestBody = "{\"name\": \"morpheus\", \"job\": \"leader\"}";

        given().contentType(ContentType.JSON)
                .body(requestBody)
                .when()
                .post("/users")
                .then()
                // TODO: Broken test example, change to 201 to fix
                .statusCode(200)
                .body("name", equalTo("morpheus"))
                .body("job", equalTo("leader"))
                .body("id", notNullValue())
                .body("createdAt", notNullValue());
    }
}

I prompted something like the below

Help me create a base test class in testng that all the classes in package io.automationhacks.testinfra.reqres.users; inherit from and that has the common setup method to set the base URL

And copilot edit understood the 2 main changes required for this \

  1. Create a new base test class
  2. Modify the existing class to inherit

Describe the change

It created the base class pretty well

Create base class

It was also able to make the change in inheritance hierarchy for the test file

Modify existing class

Accept or discard changes

I also got an option to choose if I want to Accept or Discard this change and an option to cycle through all the changes

Accept or discard changes

I also realised that I was not explicit that in this refactoring, that I wanted copilot to also remove the setup method from the test classes so I made a follow up prompt

Remove the setup method in child classes

And Copilot was quick to grasp that, it also enhanced the prompt with more context like “since it is already inherited from the base class”🤔

Remove setup method

And made the change as expected

Result after removing setup

I realised that there are few unused imports left in the file and while its easy to make this change yourself, I figured why not let Copilot do this job. 🤷

Also make sure to remove any unused imports

Prompt to remove unused imports

And I could see the imports optimised

Unused imports removed

Like a responsible engineer, I ran the test to make sure it still works with this change and it was fine.

Note: The test here fails due to legitimate reason. I had changed the expectation of the assertion on purpose to check some test infra components that I’m building when a test fails.

Test execution

Reflection: This entire workflow took couple of minutes or so and was quite fast. I did notice that you have to constantly adopt the hat of a reviewer and ensure that the change Copilot edits makes is inline with your expectations. Its important to keep the batch size small so that you can reason about the changes.

Maybe this is what future engineers would spend most of their time on. Read, understand and review code over and over. I would imagine this could be quite taxing in the long run to constantly read lots of code but don’t have anecdotes on long term use yet

Change in multiple files

Files changed

I became more excited and thought why not apply the same change to all the files in the users package and prompted below:

Please apply the same changes in all files in users package Relative path: src/test/java/io/automationhacks/testinfra/reqres/users

Apply changes to multiple files

Copilot went ahead and applied the change but also made some additional changes like change testng groups or even expected status code.

🤯 It also figured out from the comments that 204 was the expected status code and I had introduced some logic to make the test flaky and corrected that, although with wrong syntax.

So, I went back and gave a more clear expectation

Please make below changes in all files in users package Relative path: src/test/java/io/automationhacks/testinfra/reqres/users

  1. Ensure the class extends BaseTest
  2. Remove any unused imports
  3. Do not change the @Test annotation or anything in the test body

Wrong changes

Copilot did the right job and correctly updated all the files within the user’s package. 🎊

Revised prompt

Correct changes

Reflection:Copilot is not a mind reader, while it has a lot of context and can try to infer some things, you still need to be concise and clear on what you want it to do. Knowing how to prompt well and good English writing skills is a good skill to have in 2025

Ideas

This feature is exciting, it expands the use case of code generation and assistance into more of an editing workflow. This has the potential to make each developer significantly faster in repeating changes across a codebase or improving it methodically

I can see some obvious use cases for this \

  1. Given file X as an example, write a test that does Y
  2. Implement a new function in existing library code that does Z and then write tests for it

Closing thoughts

After this pair programming session, I realized below

This could be a future mode for software development and is a natural evolution of IDE’s capabilities. It brings more intelligence to the code-editing process

Yes, if we are being honest. This was not at all a complex change.

Even simple IntelliJ refactorings can achieve this change pretty fast with auto code formatting in place or even a human hand coding this.

But the idea here was the explain the feature in a simple-to-understand workflow. The capability and the accuracy of edits are quite amazing.

This could be a really useful addition to a programmer’s toolbelt and you could use it for increasingly complex tasks. God knows we need all the help we can get to manage the increasingly complex software engineering space.

Also, this reinforces that as a Senior engineer, you cannot slack off, you need to understand the underlying concept you want Copilot edits to apply to your codebase.

I see a risk that human software engineers may lose a bit of muscle memory when it comes to hand coding so it’s important to keep them sharp with doing some work yourself as well. But this has been the story of software engineering anyway. We are moving towards higher levels of abstraction and better tooling 😇

References

  1. Copilot Edits
  2. Exploring Generative AI

Disclosure: I’ll earn a small commission if you decide to purchase some of the educative.io text courses linked in the blog for your learning and growth. Their system design courses and grokking coding interview courses are arguably quite helpful for interview prep.

Comments