I was lucky enough to get an invite to the beta preview of Copilot, and I've been using it for the last month. It truly is amazing.
I'm a sucker for anything that can improve my productivity as a developer and I had a lot of ideas around Copilot – my tweet about applying the model to merge conflicts went viral. If you haven't, go read my Reflections on 10,000 Hours of Programming.
Here are my takeaways from one month of using GitHub Copilot.
Verbose Velocity – write optimized, but what about read?
After a few weeks, accepting Copilot's suggestions was second nature. I was cranking out significantly more lines of code than usual. But every programmer knows, lines of code aren't a good measure of productivity (in fact, may have a negative correlation with most productivity metrics).
Copilot is a system that's optimized for write. It has made my code more verbose. Lines of code can be liabilities. Longer files to parse, and more instances to refactor. Before, where I might have tried to consolidate an API surface, I find myself maintaining both and Copiloting mapping functions in seconds.
It's not always a bad thing. Verbosity can mean readability. It has changed the cost equation for writing code: where it might have taken too long to go the extra step to be more verbosee, Copilot let me be verbose in the same (sometimes less) time.
The implication is that Copilot is going to make us write a lot more code and we're going to need to build more tools to manage that code.
Smarter, faster refactoring of functions and variables. Better code search. Library suggestion instead of code suggestion (I discussed the problem of code discoverability in Package Managers & Developer Productivity). Maybe the same models could even be used to perform static analysis and provide refactoring suggestions.
Writing utility functions in seconds.
Every project has a file called util.*
that holds all of the general-purpose code that is really unrelated to the business logic. Generating a unique identifier, reversing a string, debouncing, or throttling requests. These functions are usually not worth pulling in a dependency for (looking at you, left-pad). Turns out that Copilot can auto-complete them in seconds, with just a function signature. Perfect for those times where you want to craft those utility functions specific to your application, but don't want to spend the time writing them.
Structure in new places – Error Handling
Copilot gives structure to Go errors. If you program in Go, you know that developers spend a lot of time error handling. A common idiom is to wrap your errors with a context string, so you can get information about the call stack, something like
return errors.Wrapf(err, "open: %s", filename)
The problem with any string like that is that it lacks consistency - developers can write whatever they want. There's not a clear strategy either – functions can return errors at multiple points, so you shouldn't just use the parent function name. And sometimes the errors don't come from a function call, they can come from a map lookup or type cast.
Since using Copilot, I haven't written a single one of these error handling lines manually. On top of that, the suggestions follow a reasonable structure where I didn't know structure had existed before.
Copilot showed me how to add structure in my code in unlikely places.
For writing SQL, it helped me write those annoying foreign key names in a consistent format like fk_users_to_teams_user_id
.
Discovering new APIs.
One of the more surprising features has been discovering new API methods, especially for popular libraries. Before, I would context switch between the API documentation and my code. Otherwise, I'd work from memory. Now I find myself discovering new API methods, either higher-level ones or ones that are better for my use case.
All in all, Copilot has been a great addition to my developer workflow. I would absolutely pay to continue using it in the future. Like any developer tool, it takes some getting used to, but once you're in the flow, you'll be coding faster than ever.