Flying with GitHub Copilot

Today I installed GitHub Copilot in VSCode and decided to see what it could do with Typescript with only natural language prompts for full functions. Have a look at the results. I’m impressed!How I TestedFor all of these examples, I typed only the comm…


This content originally appeared on Level Up Coding - Medium and was authored by David Bethune

Today I installed GitHub Copilot in VSCode and decided to see what it could do with Typescript with only natural language prompts for full functions. Have a look at the results. I’m impressed!

How I Tested

For all of these examples, I typed only the comment or comments that introduce the function. I accepted the first Copilot suggestion for the function definition, and the first suggestion for the (full) function body. I’ll use screenshots here because Medium doesn’t have proper code blocks.

The #1 Use Case for Copilot is helping with dumb, simple, boilerplate stuff that you could write but don’t have to.

Starting with the Basics

Starting with the basics, Copilot uses JavaScript’s built-in sort function to solve my problem.

One thing that impressed me off-the-bat was that Copilot tends to use “best practices” and simplified ways of doing things, which I surely appreciate! I’ll cut to the TL;DR right now and tell you this is the #1 Use Case for Copilot: helping with dumb, simple, boilerplate stuff that you could write but don’t have to.

The simple code that Copilot suggests also serves a teaching function, in that it reveals how many useful JS things work, such as sorting in this case. This one example teaches that you can .sort() an array with a custom, inline function that tests two values (in your array). A simple reading also shows how the custom function results in reordering the result (by returning -1, 1, or 0). Even if you don’t use it on real code, this “instant refresher course” acts like a live JavaScript genie, ready to show you a correct and working example if you can get things down to some simple prose.

You’ll also notice that while Copilot knows about Typescript, it doesn’t do a great job of inferring (or enforcing) types. In this example, array should not be any but rather string[]. Certainly by any decent coding style, the return type of this function should not be any, either. It is also a string array or string[], but Copilot doesn’t pick up on this yet — or at least I haven’t figured out how to prompt/force it to do so.

Removing Duplicates

Removing duplicates by testing if the item already exists earlier in the array.

Removing duplicates is a common task with more than one solution. Both of these solutions are excellent and demonstrate interesting coding techniques.

In the array example, the use of .indexOf() to test if an item already exists earlier in the array is quite clever. When combined with .filter(), it becomes an example of best practices in working with arrays by mapping, filtering, and reducing — rather than looping through them. Because these mapping functions return the modified array, the entire function consists of only the return statement.

Cleverly removing keys with duplicate values!

One of my favorite tricks for eliminating duplicates is to coalesce the values into object keys in some “testing object.” Since objects can’t have duplicate keys, no matter how many times you do this, there will only be one of each. Apparently, Copilot (and the codebases it learned from) like this technique, too, since they used it here. The testing object is newObject, which is only used to enforce the uniqueness of the values in the original. It is not returned. The original is returned with duplicate values nuked (specifically, the second and succeeding matching values), as requested. If the value is novel, it is written into the test object to remember it.

Converting to Hex

Copilot walks the array with map(), not a loop, as it should.

This simple example shows when you might use Copilot just because you forgot the exact syntax of something that you know JavaScript can do. In this case, that thing is convert something to hex. Notice the “best practices” use of .map() to walk the array.

Playing with Prompt Wording

Copilot is very good at inferring what you mean from a prompt. It appears to understand very normal, human ways of talking about code. In other words, you don’t have be methodical about how you refer to every little part. In this example, I said “an array of key, value” pairs and it knows that means an array of arrays.

Strangely, Copilot walks the array with a for loop, which is rather old skool. I like how the new object’s key and value are assigned with nested array references.

Describing the Syntax You’re After

Everyone knows array.join() takes a delimiter, right?

This is another case where I can see Copilot being very useful — not actually in your code, but nearby. I would not pollute my codebase with weird stuff just to find the name or syntax of a command like .join(). But I would type this into a blank area of the IDE so I could realize that I already know what to write in my real code. Notice that the full, working syntax is shown — faster than you can look it up on MDN or craft your own example out of one there.

That’s a Given

Copilot understands the term “given key” in the context of an object.

Copilot is good about understanding a “given” and how it relates to the giver. In the first example, it properly parses the “given” key into a separate parameter, then uses that parameter to get values to sort on. The sort itself shows a clever trick of returning the difference between the first and second items. This results in the sort returning less than 1 if the second item is larger than the first, just as in the earlier alphabetic sorting example I tried.

In the second example, we added the need to remove keys of a certain value. In this case, Copilot knows we need a third parameter for the value. After using Object.keys() to convert the keys to an array, then sorting them, this version goes on to add a .filter() to remove the items which don’t return true from the test or predicate — in this case, that the key is not equal to the verboten value.

Tricky Function Calls

Here, I wanted to see if I could trick Copilot into accepting the literal value false, as opposed to a function call that returned false. But a close inspection shows that Copilot has added the function call parenthesis to the object’s value reference with object[key]()! This enforces the function call part of my request. Pretty clever.

Creating HTML

Copilot can easily create HTML, even custom elements.

In my work creating web applications, I often need HTML elements which are programmatically created. I thought I’d see if Copilot could make some for me, including custom ones. These results are terrific! Just what the doctor ordered. Notice the correct interpretation of given again. If you talk to Copilot like you would talk to a student developer, it does quite well.

The IDE is complaining here with red squiggles because I don’t have the html (or css) prefixes imported. In a real file, I would because I use Lit all day long which makes heavy use of them. Despite being undeclared, Copilot uses them properly, including the backtick and ${className} curly brace syntax for populating the values. Very nice. And this is in a file with no Lit examples.

A Quick Type Test

Of types, sir, Copilot knoweth whereof you speak.

Copilot does seem to know what a type is, at least for this simple example. This would make defining complex types even easier, especially when Copilot starts to do a better job of understanding custom type and interface names, which it doesn’t seem to now. More on this in a moment.

Copiloting CSS

But does it do CSS? Yes!

CSS is a love/hate thing. It’s not either/or. It’s both! CSS is remarkably cool, and also remarkably terrible. I love the grid system, but it can be hard to remember all the parts. In the first example, these are all the parts. Copilot helpfully added a gap (although with the older grid-gap syntax) to make my columns not jam into each other.

In the second example, admittedly a high-flying challenge, Copilot couldn’t get us all the way off the ground. The problem here is the requirement that the result fill the screen. The only way to do this that I know of is to surround the element with a container which itself fills the screen — often with something kludgy like height: 100vh. Ick. Anyway, since Copilot doesn’t know this trick or at least didn’t show it here, the horizontally split result will not fill the screen.

Business Logic

Copilot does a good job with understanding business logic when written clearly.

This is another case of, “You should be able to do this, but…” Now you don’t have to. But, caveat emptor, the function takes your definition of the discount at face value. By writing “more than $500,” a purchase of exactly $500 does not qualify for the discount, which may not be what you want.

Copilot turned the 15% discount into multiplication by 0.85 because we only asked for the discounted price, not the discount amount. Nice. To get there, it filtered to make sure the items were above $500, then mapped the filtered results to get the discounted prices. Copilot is very good at combinations of map and filter, and you should be, too!

More Business Logic

When you write a multi-line comment, Copilot infers the function from your complete description.

Here, I tried a little more complex logic. I also used multiple lines and very casual terminology like, “each customer has a name.” I also gave instructions on how to total (but not where to put it, so it wasn’t saved). Finally, I wanted a specific key “turned on” for my premium customers. This lovely function uses .reduce() to combine all the purchases into a total. The array is walked with a .map().

Either/Or Business Logic

Copilot can scaffold “either/or” scenarios faster than you can.

Here’s an example of Copilot converting a normal person’s description of how to figure out sales regions into the correct set of if, else if, else tests. Could you do this yourself? Of course. Could you do it this fast? No. Be sure to check your work (or Copilot’s work, in this case), but a quick glance at the produced code will show that it does, indeed, assign the sales regions as I specified. I can read and verify this faster than I can write the nested statements and multi-part tests.

More Multi-Part Tests

A business logic test that includes deleting data.

In this example, Copilot honored my request to remove the event start date by deleting that key from the object as it walks through all the customers in the array. Notice that both ways of referring to the premium key (“has premium set true” and “is premium”) were understood to mean the same thing. The result reads clearly and can be understood by humans.

Running a List of Functions

Copilot properly understood my request to call all of the functions I’d already designed, on this one array.

Often it’s necessary to do several “preparation steps” on a list of items before doing some final step. Here, I wanted to see if Copilot could figure this out. When I wrote addPremium() as a function call in the comments, the next suggested comment was setRegion() and the next was setEventStartDate(). So here, Copilot is prompting me with the comments which will create the code, not just the code. Pretty slick.

The function body uses .map() to process the incoming array and correctly supplies the individual customer object as the item parameter to each function call, in the order we specified. The array is returned unchanged.

Simple Interfaces

I was delighted to see that Copilot could craft a dependent interface.

Interfaces are one of the truly great things about Typescript, so I thought I’d see if Copilot could craft some. The first one is fairly straightforward. The second one is more complex. Copilot correctly figured out that Purchase objects must have their own interface. It suggested this second line of code immediately after I accepted the first.

Notice that while in other places Copilot didn’t attempt to use custom types, in this example it clearly did and it used them well. It defined and used the Purchase type explicitly, rather than relying on a generic array. It seems like the phrase “a JSON object” was needed to generate this. Perhaps that’s why it didn’t do it elsewhere. Either that, or it can only integrate types in an interface definition and not in a function definition.

Complex, Dependent Interfaces

Here I used the “JSON object” phrase with several lines of business logic to get exactly what I was hoping for: an interface, or more accurately, a set of them. Copilot correctly inferred that we would need four interfaces and it offered to write all of them, each one after accepting the one before it. Copilot did an excellent job of turning everyday wording like “has multiple students” into an array while “student count” is a simple number. It was also not fooled by varying the wording between “each classroom” and “classrooms have,” nor by writing these directions separated from one another.

Just as with the simple example, these more complex interfaces like Classroom properly use the Student[] syntax to force an array of Student, not an array of any, whereas we don’t see that behavior on my function definition examples.

Putting It All Together

Try as I might, I couldn’t get Copilot to make an array of Schools, only an array of any.

In my final test, I hoped that Copilot would understand my Student and Classroom objects, since it had just defined them. I was disappointed that it didn’t. I even tried variations of this, such as using the wording “School objects” or even “the School object defined above.” It went so far as prompting for these comments, but didn’t deliver on the promised code, which does not use any of my custom interfaces.

So here, at the end, is somewhat of a large failing in production. The most important advantage offered by Typescript (over vanilla JS) is the ability to create and enforce custom interfaces. If Copilot makes me do that manually, it’s going to also make me introduce errors as I try to manually convert every any in this function’s parameters to a variable with a proper TS type.

Interestingly, the code will work correctly and it actually has a dependency on the previous definition. The assignment of item.teachers.length as a way to count the teachers will only work if the item with that key is an array. And it is, but only because it’s defined that way in the interface — the interface which Copilot is not adding to this code.

Conclusions and Next Steps

I enjoyed working with Copilot and giving it this little trial. In context, I think it’s very useful and I plan to continue trying it. But the most important takeaway here for me is that, like any copy/paste code (and you can think of Copilot as roboto copy/paste which changes the function and parameter names for you), you need to fully understand what it writes.

I would say a good litmus test is, “Can you understand what Copilot wrote enough to explain it to another programmer?” If not, then use the suggestion as a jumping-off point for learning (the MDN docs still rule the roost) before you add it to your production code.

In contrast, if the proposed solution is obviously correct and you can write and understand it yourself, then Copilot has just saved you a lot of time — and possibly a few bugs from typos, missing parameters, wrong tests, or other minor mistakes that we all make.

In my next round of trials, I’d like to see if I can force Copilot to do a better job of using custom interfaces, which would be the most important aspect of using it in production code.

Signing off for now, and thanks for reading! I’d love to hear your comments on Hacker News.

— D


Flying with GitHub Copilot was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by David Bethune


Print Share Comment Cite Upload Translate Updates
APA

David Bethune | Sciencx (2022-06-30T13:46:24+00:00) Flying with GitHub Copilot. Retrieved from https://www.scien.cx/2022/06/30/flying-with-github-copilot/

MLA
" » Flying with GitHub Copilot." David Bethune | Sciencx - Thursday June 30, 2022, https://www.scien.cx/2022/06/30/flying-with-github-copilot/
HARVARD
David Bethune | Sciencx Thursday June 30, 2022 » Flying with GitHub Copilot., viewed ,<https://www.scien.cx/2022/06/30/flying-with-github-copilot/>
VANCOUVER
David Bethune | Sciencx - » Flying with GitHub Copilot. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/06/30/flying-with-github-copilot/
CHICAGO
" » Flying with GitHub Copilot." David Bethune | Sciencx - Accessed . https://www.scien.cx/2022/06/30/flying-with-github-copilot/
IEEE
" » Flying with GitHub Copilot." David Bethune | Sciencx [Online]. Available: https://www.scien.cx/2022/06/30/flying-with-github-copilot/. [Accessed: ]
rf:citation
» Flying with GitHub Copilot | David Bethune | Sciencx | https://www.scien.cx/2022/06/30/flying-with-github-copilot/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.