In this article, I'll mostly cover my experience writing a bunch of backend code during my full-time job using co-pilot and will try to share a bit about how I was using it to be more productive.
I started using copilot maybe within 3months of them starting to roll it out; during the time I was working on a fresh new backend project. Which was built using Typescript. For a while, after installing it I was reluctant to use it and had only tried it out a few times for just creating a function to add two numbers and stuff.
I did try to get into it though, but found it rather slow and eventually disabled it and moved on.
Until one fine day, I had a bit of extra work and needed to squeeze it in somehow when I remembered this tool and it immediately was so much better than the last time I had used it; it was predicting correctly and quickly.
Since then I had used co-pilot extensively for a lot of my work and hobby projects.
My experience using it
I have actually used co-pilot to write large chunks of backend code; basically reading from tables, fetching from tables, query generation, converting queries into knex implementations; and the list goes on.
And I’ve messed up a lot of times as well, you see, just like autocomplete, co-pilot also impairs you someway, very slowly you start giving in to it, and start letting go of the cognitive load cause now you only think one level of abstraction above the actual implementation, you start thinking in terms of specification rather than implementation.
Let me explain, during this time, I noticed myself getting into bugs that I would normally never get into, and stuff that was really hard to explain at first, almost one of those times you’d perform the sin of questioning the compiler. Almost all of those bugs were small typos which I’d miss because I overlooked them as I myself didn't write any of those 20LOC.
Things co-pilot is really good at
- Converting very specific instruction in english into code, the less the abstraction the better
- This means, it's really good at writing singular units of code where context is limited to it’s scope
- It is also good at picking up additional information present in the file, i.e. it will intelligently pick up which variables must be used in the next operation and which other functions might be called as a side effect of the current one
- Commenting and documenting your code, you can expect to start documenting a piece of code and you’ll find co-pilot brilliantly fill in the details for you by understanding the implementation
- You can sort of get it to do chores for you as well, I had a bunch of code where there was a SQL prepared statement and I had the task to refactor it out, and co-pilot helped beautifully. Ever since then, I got into a habit of playing with queries in the MySQL console, pasting it in the ide, and making co-pilot write the knex equivalent for it.
Things it isn’t good at
- In my personal opinion, co-pilot isn’t very good with magic code, i.e. code that is heavily abstracted
- It isn’t good with libraries or patterns which are uncommon. Let’s say your org has a big internal library which is very unique, you can’t really expect copilot to predict it that well; however, if you have similar implementations in the same file you can start typing it out and co-pilot sorta fizzy finds it’s way to whichever implementation you'd wanna use.
Best use cases right now
- Use it as google/StackOverflow; whenever there’s a piece of code that you have already written once in your life, and know how it works you can easily describe it as a comment and co-pilot should lead you there. The co-pilot is a beast at this.
- Refactor and chores. First of all, make sure you read each and every piece of code generated by this, as small diversions from the expected code can make the code behave in ways you wouldn’t be able to catch very quickly, and it should be tough as you are not the person who implemented it. I’d say co-pilot mostly gets it right, even contextual information, I’ve seen it refactor a nested SQL query into beautiful knex.
In the gif below, you'll see me asking co-pilot to create a function which gets users from somewhere, the most common usecase being fetching it from a relational database. After the first suggestion, you'll notice I changed the function signature a bit hinting at a possible pagination implementation, after which co-pilot very quickly understood the context and implemented the pagination code in the query on it's own.
This is how you can calibrate in scenarios where you need an unconventional or uncommon code you can either open up the co-pilot suggestion list (Ctrl Enter) and pick one that's matches.
If you don't find the exact code you're looking for, get one that is closest and then just write the rest on your own, however, if you don't even find a resembling piece then maybe you're describing a really big and abstract code and maybe you can try breaking it into subtasks and asking github to fill in the code per atomic operation.
In the gif below, you will see I am not making co-pilot write a new piece of code, rather making it do refactoring work, I really enjoy taking it's help while doing stuff which is necessary but sometimes boring.
In the gif below, you'll see that I wanted to have a function which uploads a local file to a s3 bucket, however I wanted to implement it using
boto3.resource and not
boto3.client; in this scenario changing the function definition doesn't make sense, so we add a comment to guide co-pilot into the right direction.
After using co-pilot for a while, you guys will learn this sense of guiding copilot; I’ve eventually developed this way of setting up the stage around the part of my file where I’d want co-pilot to fill in some lines, and if you do the setup just right, you can guide co-pilot really well to predict the exact code that you’d want to generate.