Github Copilot was the first mainstream AI coding assistant. First announced in June 2021, it became publicly available in June 2022. It has since become the tool of choice for software developers, whether they’re professionals or enthusiasts, who want to leverage generative AI for writing code.
I started experimenting with Copilot two and a half years ago as a learning exercise. At the time, Copilot was a simple but powerful inline coding assistant. Since then Copilot has evolved, rapidly adapting to the ever changing GenAI space. First there was Copilot Chat, then Agent Mode became increasingly popular.
For more than two years, I’ve been assigned to a project where Copilot is the only coding assistant allowed. This led me on a learning path. My objective was to find how best to use the tools available in order to improve my team’s delivery while maintaining the coding standards that we set for ourselves.
In this article I'll share how the way I’ve used Copilot on a real life project has evolved over time as both the situational demands and the tool itself has changed.
Tales from the AI trenches
Throughout this piece I’ll illustrate my thoughts with experiments I ran on the job. The project spans several microservices (Java with SpringBoot) and a front end (Javascript with React) that evolved in a distributed monolith.
I'lll be focusing on one repository that is the archetype of technical debt. Amongst other things, the tech stack is outdated (React 16 last received security updates in 2020) and the code has only been partially migrated to Typescript.
Another thing worth mentioning: my team uses IntelliJ as our main IDE; keep that in mind when I’m talking about highs and lows!
The early days of GitHub Copilot
I joined my current client in August 2023. The tech stack is fairly common: Javascript/Typescript and React on the front end, Java and SpringBoot in the back end. The IDE we use is IntelliJ. It works very well with the tech stack and has plenty of useful tools, including a Copilot extension.
At the time I had limited experience with Copilot, using it mostly on learning projects. I wrote a couple of articles on my learning path (part 1 and part 2). My first experience on the job was very similar to the conclusion I drew in the articles. I really enjoyed what I called ‘going with the flow’: start coding as I would without AI and simply see what the tool suggested. Quite often the suggestions were in line with my expectations; I’d hit ‘tab’ to accept them and carry on.
I found that over-engineering prompts to solve larger problems at once didn’t suit my workflow. It required significant specification in my prompts which I found took more time than actually writing it myself.
In these early days, ‘auto-complete on steroids’ was often used to describe Copilot, sometimes meant in a derogatory way. For me, though, this approach was the way to go and worked quite well.
The chat interface
The next feature was Copilot Chat. It took a bit of time to come to IntelliJ (almost six months after VSCode) but it then brought the chat experience within the IDE. At the time the available features were lackluster: it was merely ChatGPT inside the IDE. The context window was small (only a few opened files) so it wasn’t capable of large scale investigation.
It was nonetheless still an improvement. I no longer had to switch to the browser to ask simple questions; I could do it in the IDE. This saved a few seconds when I needed to find the correct syntax or the latest version of an API. However, this was really the full extent of the benefits.
So, all in all it was nice to have but it wasn’t groundbreaking by any means. The true revolution came later.
The agent era
The agent was the next big thing — at least according to the community when various agent tools first emerged. Claude and Cursor, among many others, introduced the concept of autonomous agent; it was supposedly capable of reasoning, planning and carrying out implementation with only minor supervision.
However, the evolution of the chat in agent mode in IntelliJ took its time. It was first capable of reasoning with the context to modify a single file. Then it was capable of modifying multiple files, which allowed more complex workflows.
In theory it was also capable of running tools inside the IDE, but IntelliJ was seriously lacking here at the time; interaction between agents and tools was pretty dreadful. Simple operations such as running tests often required human interaction; you needed to copy the output to the chat window. Even interacting with the built-in terminal required those manual steps.
But it wasn’t all that bad. When working on some opened files, you could use the chat to refactor code or analyze issues in a way that was much smoother than with Copilot in its autocomplete era. Yes, it still required the relevant files opened in the context and was struggling using tools, but for me it was a much smoother and enjoyable experience than prompting through comments.
Instruction files helped to streamline prompting by populating context with relevant information and guidance. However, the context window was somewhat narrow, which meant tasks with greater scope were more challenging.
Overall I was very disappointed. The agentic revolution appeared to be just another big lie. Sure, there were some benefits, but I couldn’t see where all the hype came from. This confused me because the community was so enthusiastic about the agentic flow.
A tale from the AI-assisted trenches: Migrating a backend for frontend to Typescript
The aforementioned monolithic repository is split in two: the React client and a backend for frontend (BFF). The client had been (partially) migrated to Typescript but the BFF was still pure Javascript. I decided I wanted to enable Typescript with the help of an agent.
I started asking what would be necessary. Copilot listed a few requirements such as adding the dependencies in package.json, adding configuration files. It also stated the project should be migrated from commonJS to ESM. That plan made sense. The ESM migration was a massive hallucination though, which I didn’t know at the time, so I went along with it.
Copilot then implemented the first steps, adding dependencies and upgrading configuration files. That looked alright, but then came the ESM migration. Copilot needed to edit all the source code to change imports. A simple but repetitive task. This is where it failed massively. The IDE plugin stopped after every couple of files. I had to prompt it every time to continue. Sometimes it even froze for a while and I had to cancel and reprompt. Copilot also often failed to run tools (such as compile or tests), so I had to run those manually and copy the output to the prompt.
Overall it was an awful experience. I got fed up with telling Copilot to continue every other file; after an hour or so I just stopped and cancelled the whole operation.
A game-changer: Copilot CLI
Then came the CLI tool; this, for me, is when everything changed. Copilot CLI is mostly a copy of Claude and other CLI coding agents that came after. At the time of these experiments it was still in the early stages. It only had basic features (i.e. instructions) and nothing fancy like the custom agents that we have in 2026.
However, it did deliver impressive results. I was able to do tasks that failed miserably with the IntelliJ plugin. It was much better at reasoning at the project level. And, most importantly, it was capable of interacting with tools, reading their output and reacting. It didn’t get stuck after editing two files, it was properly autonomous (after being allowed to run programs or commands).
A tale from the AI-assisted trenches: Migrating a BFF to typescript (again)
With the CLI available, I decided to try the BFF migration to Typescript again. I started the CLI from the BFF folder and asked a fairly similar prompt to the previous experiment. Copilot came up with a plan; this one, though, didn’t include the ESM migration. I asked why and learned it wasn’t actually required.
I then had Copilot execute its plan; after a few minutes, it was done. It ran the tests, linted and built the application. Everything was there and seemed to be working fine. This was, to be clear, just a prototype. I used it to report to the team and demonstrate how easy it was to migrate the BFF to Typescript.
We later decided to productize the prototype. I used the CLI again and started from scratch, this time with more guidance (such as “look at the client’s configuration”). Again, within an hour it was all there: code was built, tests run, code linted and scripts adapted. Every change was committed and ready to be pushed to main.
From there, I started experimenting with an agentic workflow: having the agent make a plan, discuss, refine, then execute. That did require trial and error to get it to work in small batches and avoid large scale modifications (a nightmare to review), but having good instructions in the repo and providing the correct information and guidelines in the prompt significantly increased the quality of the output. In other words, it required small incremental changes, and committing small and often.
So far, Copilot CLI is still catching up with Claude and Cursor. However, it’s now going in the right direction. I had very good results and I’m enjoying the experience. It boosted my productivity when working on refactoring tasks and allowed me to prototype large scale changes within hours. This would have taken days to do manually. The agentic revolution appeared to have arrived.
One final tale from the AI-assisted trenches: Migrating from React 16 to 18
This is a daunting task. Doing it manually requires significant research and involves trial and error. There was, though, lots of talk online claiming that Copilot is ideal for framework migration or other similar tasks.
I knew a few items would be problematic. For instance, some tests were using enzyme — now deprecated and not supported by React 18. I was also worried we had to migrate to a new version of react-router at the same time.
So, I started CLI and began asking questions. Copilot returned further questions to clarify my requirements. It gave me options for enzyme, including mentioning a compatibility library that would avoid the rewriting of all the incriminated test files. I liked that idea so I went with it. The plan didn’t mention any react-router migration so I asked about it; Copilot told me this wasn’t required as part of the migration but it was strongly advised as something that should follow soon after. So, I decided to ignore it for the time being.
With the plan ready, Copilot continued. It took several hours, mostly because I was doing other things at the same time and missed requests for authorization to run a tool. I also paused for a while to cook and spend time with my daughter. I continued in the evening and it finished, seemingly successfully.
The following day I reviewed everything. Copilot had updated the dependencies, adapted the configuration, tweaked production and test code to work with the updated library, run tests to verify its work and identify new errors.
There were, though, a few kinks that Copilot pointed to timing differences in React internals between the two versions. Some complicated tests were failing that Copilot couldn’t fix. Running the app locally showed some strange behaviour on a couple of pages, but it was mostly okay. Everything started correctly and the application was behaving almost as expected.
This was a prototype to test the feasibility of the migration. Despite the kinks, I considered this experiment a tremendous success. The migration was almost entirely successful and Copilot also noted some things to look at when fixing the broken tests. This saved a lot of time and proved the task is doable.
The tool evolved, but so did I
In this period of time, Copilot changed a lot. New features were added and new products emerged, such as the CLI. They became easier to configure and tweak with custom instructions.
The tools changed, but so did I. I learned and became better at prompting, at refining questions and at articulating my requirements. I also learned to use configuration files to avoid repeating myself.
My most recent experiments have been heavily influenced by earlier ones; failures were responsible for the many wins that came after.
Wrapping up
So, where does all of that leave us? Is there a superior tool to rule them all? The answer is not that easy. It really depends on what you are trying to achieve.
Am I writing code in the IDE? If so, the inline coding assistant is my best friend. Yes, it will make mistakes, but it will quite often generate the code that I was about to write manually. Depending on context, it should also pick up on best practices I’ve already established. While I have to be vigilant about what’s generated, working in small iterative chunks counters some of the risks. And if I have some pending question, I can use the chat in ‘Ask’ mode to discuss options.
If I want to refactor a file, the chat in agent mode is very powerful. A careful prompt or a well crafted instructions file will be effective at migrating a file from one framework to another, and removing duplication in tests or production code. Ideally the IDE will also run the tests or the linter and process the output, but at present I find IntelliJ lacking here. Tools aren’t yet well integrated with the agent. I suspect VSCode might be better, because Copilot has developed there first. I haven’t tested it yet.
If you want to work on the repository as a whole, the CLI is fantastic. With a well-made plan (that the agent can also generate) it’s able to work autonomously to upgrade configurations, research and modify code, refactor, run various command line tools, process the output and react accordingly. The agent will keep a TODO list to keep track of its progress and be able to iterate over it.
Both agents (IDE and CLI) are extendable with custom commands and instructions files. This can help you streamline recurring tasks.
Maybe, then, there is an easy answer: use the right tool for the job! It’s simple but very much applies to the ongoing wave of GenAI tools.
What next?
Custom agents and agent skills are trending. At the moment I’m currently experimenting with them and, so far, have seen great results. I’ll continue in that direction for now, at least until something else emerges that demands attention!