After receiving an invite for the GitHub Copilot Technical Preview I decided to try out the feature by building a browser-based Tetris-clone. This post will look at how Copilot offers suggestions while building software.
The completed result is below, and you can see the source here.
I’ll start from a blank slate and am looking to build a working app as quickly as possible. I won’t look to optimise the code for performance - rather I’m looking at a first pass that feels similar to the famous block game. I’ll use TypeScript for code and pixi.js for rendering, and hopefully Copilot will write a bunch of the code for me.
I install and authorise the vscode extension for Copilot, create a new vite project, and open the IDE:
npm init vite@latest tetris --template vanilla-ts cd tetris npm i pixi.js code .
From the scaffolded
main.ts that vite creates, I remove the
app.innerHTML setter, and rename
#pixi. My starting point looks like this:
import "./style.css"; const element = document.querySelector<HTMLDivElement>("#pixi")!;
The First Suggestions
In the screenshots that follow, grey text is a suggestion from GitHub Copilot. I type
const and immediately Copilot gives me the first suggestion:
This suggestion is impressive for a number of reasons:
- It’s correct - I was going to write that
- The file does not yet have imports for pixi.js
- The correct module alias is suggested:
- The correct class name is suggested:
Hitting tab accepts the suggestion. I know I want pixi to resize based on its parent element, so intellisense gives me
resizeTo as I type it but upon selecting it I get another suggestion from Copilot:
This is also correct, and, references the existing variable. Hitting tab to accept the suggestion completes the line, but this is not executable code because an
import is still required. I move to the top of the file, type
import * as PIXI and another correct suggestion is offered:
Pondering whether Copilot was clever enough to inspect
node_modules I tried the above steps again on a new repo without installing pixi. I get the same correct suggestions, and intellisense on the
PIXI.Application options, even though the package is not installed - the screenshot below has a red squiggly under
pixi.js because it’s not installed.
I go to the bottom of the file, hit enter, and again get a correct suggestion:
While I type, partial lines, and sometimes entire lines, are constantly suggested with striking accuracy. It continually uses and references what I’ve already written and appears to know as much pixi syntax as I do. Were I a pixi beginner, suggestions like
app.screen.width introduce me to API that otherwise can take some time to search for and learn. So far, I’m quite impressed.
The player pieces in Tetris are called Tetrominos, so I’ll create an abstract class that represents their colour and shape and create a concrete class for each of the seven block types I, O, T, S, Z, J, and L. I create a new file, type the name of the class, and I get my first nonsense suggestion:
At this point my pessimism has me expecting a lot more of these. However, when I discard the suggestion and start typing what one of the concrete implementations might look like, I get a startling result where each of the concrete classes is correctly suggested at once:
My intention is to have a static factory method on the base class that will return random Tetrominos. I type
static and it turns out Copilot feels the same way I do:
The next suggestion implements the entire method for me!
Each of the concrete classes need to have their block layout specified in the constructor. Copilot was able to suggest correct block layouts for each of the seven Tetrominos, however this needed tweaking afterwards as some used
-1 and others
0 as the lowest
On the base class,
moveDown were correctly named and implemented via a sequence of pressing
Enter followed by
Although I took a different approach later, I originally implemented a
TetrominoView class responsible for drawing Tetrominos. Copilot suggested a constructor with using Parameter Properties - a feature I was unaware that TypeScript had. It additionally suggested a very accurate
draw function. The only part I needed to change was to translate the sizes to pixels:
With Tetrominos being created and drawn, I want to implement the
rotate method. Geometry is not as brainless as bashing keys so I’ll write tests to help with this feature. I added jest to the project and started writing the first test. Copilot suggested an accurate, but otherwise low-value test:
My intention was to have Copilot write
rotate for me, but initial suggestions were verbose and inaccurate, and admittedly it took me too long to remember you don’t need
cos when you’re rotating 90 degrees. Eventually, with a comment added to help suggestions, I got a concise result:
Test writing suggestions weren’t great until I had a bit of repetition in the file, after which it could suggest accurate tests even if some of them missed a closing bracket:
Listening For Keys
window.addEventListener it correctly suggested the event:
And after accepting the suggestion, correctly suggested the implementation for arrow keys:
Moving my cursor to the bottom of the implementation, it then suggested adding
game.drop(), a feature I had not thought of yet! Note the red squiggles on methods that don’t exist yet - they are correct, but yet to be implemented:
When adding the
drop method all I was certain of was that I’d add a new block at the end. So I added that, moved the cursor above and Copilot suggested the rest of the implementation correctly one line at a time:
After spending around eight hours building the game and messing around with Copilot I can comment on both its strengths and weaknesses. Ultimately it comes down to how frequent and accurate its suggestions are. Most social media screenshots I’ve seen are of nonsense suggestions, but I only had one or two occasions when that occurred during development, and it only takes a second to laugh move on.
When suggesting full functions I found it could miss a bracket - especially in tests. But, adding a single bracket after accepting a suggestion is still quicker than typing the suggestion myself. It will suggest modules that are not yet
imported and, accepting the suggestion will not add an
import - however a
CTRL+. afterwards can Quickfix this. It doesn’t offer suggestions during multi-cursor activities. I use prettier to
formatOnSave and doing so sometimes has Copilot suggest duplicate lines that already exist but have been formatted across lines. It also gets whitespace wrong a bunch, but
formatOnSave fixes that anyway.
The frequent and, sometimes disturbingly, accurate partial and full-line suggestions are amazing time savers. The discoverability of new API and language features while typing code means you can learn like you’re pair programming. The occasional full function implementation is just icing on an already delicious cake.
Importantly, suggestions don’t rely on existing code to be syntactically correct. The code doesn’t have to build before accurate suggestions are available. I don’t need the correct
imports to be present. This means the tool can immediately slip into my way of coding - add some lines here, move up or down, tweak this, write half a line here, it doesn’t matter - the suggestions keep coming through.
Overall I’m very impressed with Copilot and quite happy to include it as part of my development toolset. I chose a project for this post that I presume has plenty of implementations across public repos, and Copilot’s knowledge of Tetris does appear to validate this presumption. I’m looking forward to seeing how it will perform across other project types and codebases in the future.