There are a number of AI tools for developers emerging on the market. But in my mind GitHub Copilot stands above the rest because of its usability, seamless IDE integration, and remarkable enhancements to developer productivity
Copilot offers a variety of AI tools that have radically streamlined my experience as a software developer. I've used it to generate code, tests, and even simple applications. It's also great for debugging, refactoring, and documenting existing code.
Weirdly, using Copilot has caused me to develop features faster than business stakeholders can review them.
It's important to note that AI tools, including Copilot, can be blatantly wrong, apologize (or not) when corrected, and then confidently produce the same error. But as long as you're aware of the downsides of AI tools, and have enough coding knowledge to recognize when they're incorrect, you can mitigate them on the path to substantially improved productivity .
For setup and basic usage of Copilot, check out the docs . You can add on Copilot to an individual or business account, and there's a free trial and reasonable pricing after the trial.
After adding Copilot to your GitHub account, you'll want to install the plugins for your IDE and log into GitHub to access Copilot.
In this article, we'll use these Visual Studio Code extensions :
GitHub Extension | Description | Preview |
---|---|---|
Copilot | AI pair programmer with in-IDE code suggestions | No |
Copilot Nightly | Nightly build of Copilot, includes latest changes | No |
Copilot Labs | Experimental features in sidebar | Yes |
Copilot Chat | Interactive chat in sidebar, part of Copilot X | Yes |
Copilot Voice | Voice assistant | Yes |
Notes:
Before jumping into some key use cases for Copilot, a quick note on privacy: Basically, if you trust GitHub to host your source code, you can likely trust what they do with your Copilot prompts and code snippets. [See their FAQ docs and Privacy Statement .]
The uses cases for GitHub Copilot are numerous, especially when you add in t he preview features of Labs, Chat, and Voice. Using Copilot's features can really streamline the development process.
Here are some great ways to leverage Copilot extensions:
Category | Extension(s) |
---|---|
Code Generation | Copilot, Copilot Nightly, Copilot Voice |
Explaining Code | Copilot Labs, Copilot Chat, Copilot Voice |
Language Translation | Copilot Labs, Copilot Chat |
Debugging | Copilot Labs, Copilot Chat |
Refactoring | Copilot Labs, Copilot Chat |
Test Generation | Copilot, Copilot Nightly, Copilot Labs, Copilot Chat |
Code Reviews | Copilot Chat |
Voice-Driven Development | Copilot Voice |
Copilot is known for its code completion features. If it's given a comment in the code or you type the beginnings of a line of code, Copilot will suggest a solution that you can accept, ignore, or explore alternatives. It's referred to as "ghost text".
Once you have the Copilot extension installed and you're logged in, code completion is as simple as typing instructions and hitting the Tab key once the suggestion comes up.
A comment prompt in Ruby that Copilot uses to create a method and invocation
Taking code completion one step further, Copilot Chat can take a single prompt and generate instructions for building an entire application.
I asked it to create a REST API in Node with CRUD functions for "products", and it generated functional code. Impressive overall. Given the same prompt, ChatGPT also gave me instructions for how to run the code, which was nice.
Copilot Voice can also write or edit code based on voice commands. Most of the time, using the keyboard with Copilot code completion is faster than dictating lines of code with Voice. But it's compelling to think about using Copilot Voice to create and run code without having to type or stare at a screen.
I often have to stare at a screen to figure out what a block of code is doing. Sometimes it's legacy code where I don't understand the business context. Sometimes it's using a particular library I'm not used to. Sometimes I curse the person who wrote it, even if it's me.
Below is some code generated by Copilot Chat from my "REST API in Node..." prompt from above:
app.get('/products/:id', (req, res) => {
const id = req.params.id;
const product = products.find((p) => p.id === id);
if (product) { res.json(product); }
else { res.status(404).json({ error: 'Product not found' }); }
});
I took this code and fed it into the "Explain" feature in Copilot Labs. Here's the response...
Here is the explanation for the code above:
This is a pretty straightforward segment of code, and the response is clear and straightforward as well. It would be easy to add this as documentation in a code comment above the function.
Another good example is regular expressions. I understand how regular expressions work but I don't often have the patience to figure out what a particular regex is doing. I fed Copilot Chat this giant regex statement for email validation:
(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.)(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
It explained the regex at a high level, indicating it was broken into the local and domain parts, representing before and after the "@" sign in an email address. Then it described allowable characters in each part. Quite helpful.
Developers are often required to quickly learn new languages and frameworks. Copilot Labs and Chat make this pretty easy. Here's an array of products in JavaScript and Ruby that was converted by Copilot:
let products = [
{ id: '1', name: 'Product 1', price: 10.99 },
{ id: '2', name: 'Product 2', price: 19.99 },
{ id: '3', name: 'Product 3', price: 7.99 }
];
products = [
{ id: '1', name: 'Product 1', price: 10.99 },
{ id: '2', name: 'Product 2', price: 19.99 },
{ id: '3', name: 'Product 3', price: 7.99 }
]
This is a very simple example, so let's try making this into a class and going the other way (from Ruby to JavaScript):
class Product
attr_reader :name, :price
def initialize(name, price)
@name = name
@price = price
end
end
class Product {
constructor(name, price) {
this.name = name;
this.price = price;
}
}
This checks out. For good measure, I tested TypeScript as well, and it worked perfectly, too.
This is a very handy feature. You can give Copilot Chat or Labs problematic code or error messages and it will indicate what it thinks the issue is.
Copilot Labs "Fix Bug" Brush Feature
I can't tell you how many hairs I've pulled out because I couldn't find what amounted to a syntax error. Copilot's ability to fix bugs just makes me happier and hairier.
You can also give Copilot Chat an error message with no other prompt and it can tell you the issue. I arbitrarily removed a dot from the Node REST API code:
products[productIndex] = { id, ..req.body };
I copied/pasted the error message in Chat, and it responded with...
Looking at the code excerpt you provided, it seems that the issue is on line 49 where there is a syntax error in the object literal. The ".." beforereq.body
is causing the error. It should be just one dot instead of two.
This is both correct (the line and source of the error) and incorrect (that it should be only one dot). It then gave me a code snippet with three dots, which is also correct.
As I mentioned at the outset, you've gotta double-check AI's work. Still, I'm impressed by Copilot's ability to parse an error message, describe what it thinks is wrong, and suggest a fix.
Another great use case is refactoring. Consider the code below:
def price_with_discount
price - (price * discount_rate)
end
This simple price calculation doesn't factor innil
values, strings that should be numbers, or reasonable values forprice
ordiscount_rate
. We need the code to do this:
price
is an integer or float (not nil)price
is not negativediscount_rate
is a float (not nil)discount_rate
is not negative and less than or equal to 1Copilot Chat handles this with ease...
def price_with_discount(price, discount_rate)
raise ArgumentError, "Price must be a positive number" unless price.is_a?(Numeric) && price >= 0
raise ArgumentError, "Discount rate must be a float between 0 and 1" unless discount_rate.is_a?(Float) && discount_rate >= 0 && discount_rate <= 1
price - (price * discount_rate)
end
Using Copilot to generate tests is technically a subset of "Code Generation" above, but I felt it was so useful it warranted its own mention.
Generating unit tests for code can be tedious if you're trying to cover happy and sad paths, primary use cases and edge cases. Ourprice_with_discount
code above needs test cases to ensure an error is raised ifprice
ordiscount_rate
don't meet the requirements. In addition, we might want to test the following:
price
without adiscount_rate
price
with a reasonablediscount_rate
(for example, a 10% discount)price
with a 100%discount_rate
(for example, if we want to give the product away)With these and our previous error cases, here's what Copilot Chat generated, after a little fine-tuning (re-prompting) and switching from Minitest to Rspec.
RSpec.describe Product do
describe "#price_with_discount" do
context "when given a valid price and discount rate" do
it "returns the price with the discount applied" do
product = Product.new("Test Product", 100, 0.2)
expect(product.price_with_discount(product.price, product.discount_rate)).to eq(80)
end
end
context "when given a nil price" do
it "raises an ArgumentError with an appropriate message" do
product = Product.new("Test Product", nil, 0.2)
expect { product.price_with_discount(product.price, product.discount_rate) }.to raise_error(ArgumentError, "Price must be a positive number")
end
end
context "when given a price less than 0" do
it "raises an ArgumentError with an appropriate message" do
product = Product.new("Test Product", -100, 0.2)
expect { product.price_with_discount(product.price, product.discount_rate) }.to raise_error(ArgumentError, "Price must be a positive number")
end
end
context "when given a nil discount rate" do
it "raises an ArgumentError with an appropriate message" do
product = Product.new("Test Product", 100, nil)
expect { product.price_with_discount(product.price, product.discount_rate) }.to raise_error(ArgumentError, "Discount rate must be a float between 0 and 1")
end
end
context "when given a discount rate less than 0" do
it "raises an ArgumentError with an appropriate message" do
product = Product.new("Test Product", 100, -0.2)
expect { product.price_with_discount(product.price, product.discount_rate) }.to raise_error(ArgumentError, "Discount rate must be a float between 0 and 1")
end
end
context "when given a discount rate greater than 1" do
it "raises an ArgumentError with an appropriate message" do
product = Product.new("Test Product", 100, 1.5)
expect { product.price_with_discount(product.price, product.discount_rate) }.to raise_error(ArgumentError, "Discount rate must be a float between 0 and 1")
end
end
end
end
One test for valid input, five for invalid input. All six work, and I didn't have to write them!
One feature of Copilot X is Copilot for Pull Requests . Here are some of the key features:
Formerly known as "Hey, Github!", Copilot Voice allows you to use natural language prompts to interact with your code. It looks impressive, boasting these capabilities:
GitHub is rapidly producing revolutionary developer productivity tools with its suite of Copilot extensions. It's increasing my joy in programming and decreasing my time spent on mind-numbing tasks. I would encourage you to keep track of enhancements to Copilot as they're happening quickly.
Ignore click-bait promises of a "10x productivity gain", but don't ignore the research of Copilot's impact on developer productivity and happiness .
Spend some time with Copilot tools trying out the use cases above, and I think you'll be surprised by its effect on your productivity and happiness.