I subscribed to GitHub Copilot for the first time a couple of weeks ago. I watched a few YouTube videos before that to help me understand what GitHub Copilot could do for me.
From the outset I was sure that GitHub Copilot could be a game-changer for me as an independent developer. It makes me much more productive in languages I know well. It helps me understand and become productive in languages that I’m just learning. I find the way it explains third-party APIs and programming language modules extremely helpful.
But in several videos I saw on the GitHub YouTube channel, GitHub Copilot team members said, “GitHub Copilot is Not Autopilot.” They repeat this phrase often. What do they really mean?
When they say, “GitHub Copilot is Not Autopilot,” the GitHub Copilot team means that Copilot works with me as a pair programmer; It is not designed to be the lead developer while I assist it.
Why am I “Pair Programming” When Working With GitHub Copilot?
Since I never worked in a pair programming environment before, I had to understand pair programming basics in order to get the most productive use out of GitHub Copilot.
I went back to the very beginning of Pair Programming– 1999– and looked at what the leading researchers said about the technique. In All I Really Needed to Know about Pair Programming I Learned in Kindergarten1, Laurie Williams and Robert Kessler said, “Pair programming is a style of programming in which two programmers work side-by-side at one computer, continuously collaborating on the same design, algorithm, code or test.” In Strengthening the Case for Pair Programming2, they said, “One partner, the driver, controls the pencil, mouse, or keyboard and writes the code. The other partner continuously and actively observes the driver’s work, watching for defects, thinking of alternatives, looking up resources, and considering strategic implications. The partners deliberately switch roles periodically.”
Calling GitHub Copilot my pair programmer makes sense, when I consider that Copilot watches me when I write code comments. Then it uses my comments as a prompt to generate program code suggestions for me.
Copilot Helps Me Start Doing Simple Tasks Quickly
I wanted my first development effort with GitHub Copilot to be a simple proof-of-concept. I decided to create a Python script called imap-account-delete-from-sender.py that:
- connects to an IMAP email account,
- deletes all email from senders matching an email address pattern, and
- expunges the deleted emails.
My use case for this script is to prune hundreds of email notifications I receive each year. These emails are relevant at the time they are sent, but obsolete shortly thereafter.
Since I’d never written anything with Copilot’s help before, I thought perhaps I should use a Python module to determine the size of the contents of a mailbox first. That would show that code GitHub Copilot helped me write was working without destroying any old, meaningful emails. I asked Copilot in the Chat pane:
![GitHub Copilot prompt: can I use imaplib to report the size of a mailbox?](https://daveaiello.com/wordpress/wp-content/uploads/2024/05/Image-5-30-24-at-12.18 AM.jpeg)
Copilot responded:
![GitHub Copilot response: Yes, you can use the imaplib library to report the size of a mailbox....](https://daveaiello.com/wordpress/wp-content/uploads/2024/05/Image-5-30-24-at-12.18 AM-1.jpeg)
GitHub Copilot is pretty excellent at creating the initial structure of a script like this. I pasted this code into a VS Code text editor, replaced the server details and credentials as directed, and ran the Python program. I didn’t save the output, but it came back with an answer that the mailbox I had opened was about 1.5 Gigabytes in size. So that effort was a quick win.
Quick Wins Result in Iterative Pair Programming
I moved on to asking GitHub Copilot to modify this Python script to search for email from one sender:
![GitHub Copilot prompt: How do I iterate through a mailbox and select only the messages by one sender?](https://daveaiello.com/wordpress/wp-content/uploads/2024/05/Image-5-30-24-at-12.02 AM.jpeg)
Copilot gave me this response. This version of the program calculates the size of the messages sent to me by the user with the address sender_email. The recommended code does what I asked. But I circled the “fetch command” line that Copilot referenced to illustrate an important point that I am going to make later.
![GitHub Copilot response: Yes, you can use the imaplib library.... then use the fetch method to get the size of each email....](https://daveaiello.com/wordpress/wp-content/uploads/2024/05/Image-5-30-24-at-11.06 AM.jpeg)
The code that Copilot is helping me write is getting closer to my original goal for the script. Now I ask it how to delete the messages we have previously selected:
![GitHub Copilot prompt: How can I delete a specific email from a mailbox using imaplib in Python?](https://daveaiello.com/wordpress/wp-content/uploads/2024/05/Image-5-30-24-at-6.54 AM.jpeg)
The answer I got reflects suggests change to the same script:
![GitHub Copilot response: To delete a specific email from a mailbox using imaplib in Python, you can use the uid method with the STORE command....](https://daveaiello.com/wordpress/wp-content/uploads/2024/05/Image-5-30-24-at-6.55 AM.jpeg)
Incorporating this code makes imap-account-delete-from-sender.py destructive for the first time. And the production email account I’m running it on is very large, so I want to add some print statements to the code to show what’s happening as I run the code through the Python debugger.
Copilot Has Some Amazing Contextual Inference Skills, But They’re Not Perfect
Inside of the for id in mail_ids:
loop, I decide to insert the following Python statements. Some were from the previous Chat dialog. Others Copilot recommended by from my inline comments:
# Parse the email headers to get the sender's email address
email_message = email.message_from_string(raw_email)
from_address = email.utils.parseaddr(email_message['From'])[1]
# Parse the email headers to get the date
date_str = email_message.get('Date')
# Get email subject
subject = email_message.get('Subject')
# If the sender's email address matches the pattern, mark the email for deletion
if sender_pattern.match(from_address):
print(f"Deleting email: {subject} from: {from_address} with date: {date_str}")
try:
imap_server.uid('store', id, '+FLAGS', '\\Deleted')
email_deleted_count += 1
except imaplib.IMAP4.error as e:
print(f"IMAP error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
else:
print(f"Skipping email: {subject} from: {from_address} with date: {date_str}")
When I ran imap-account-delete-from-sender.py and tried to delete messages from the from_address “notification@leagueathletics.com”, these two print statements in the if
and else
blocks should show me messages like this:
Skipping email: Test Message from: dave@daveaiello.com with date: 2020-01-01
Deleting email: Game Added on 01/15/2020 from: notification@leagueathletics.com with date: 2020-01-01
Instead, what was being displayed was output like this:
Skipping email: None from: dave@daveaiello.com with date: None
Deleting email: None from: notification@leagueathletics.com with date: None
Why was this happening?
The issue relates back to the line of code I circled in one of my previous screenshots.
![GitHub Copilot response: Yes, you can use the imaplib library.... then use the fetch method to get the size of each email....](https://daveaiello.com/wordpress/wp-content/uploads/2024/05/Image-5-30-24-at-11.06 AM.jpeg)
GitHub Copilot Doesn’t Necessarily Recognize All of the Nuances of The Code It Helped You Write
The third parameter of the fetch
command shown above determines what information is available to Python statements further inside the for
loop.
I discovered this by half a dozen rounds of trial-and-error, running the code over and over through the Python debugger, and eventually looking at documentation for every method my code used in the imaplib
Python module.
In the back-and-forth communication between me and GitHub Copilot that got us here; In the times I highlighted the for
loop and asked Copilot to “/fix this so subject and date_str are populated”, Copilot never told me that I needed to load up the third parameter of the fetch
command with header attributes for subject and date. Then the rest of the code shown above would work.
Considering Copilot’s comments in a previous Chat session that begin with, “Here’s how you can modify the code to” do something new, I expected Copilot to be able to recognize that adding a print statement that included previously unused parts of the email message header would necessitate first having those parts of the header in the data
variable.
What Doesn’t Kill Me Makes Me Stronger3
GitHub Copilot is an incredible tool in many ways. But it is not yet the perfect AI pair programmer, and may never be.
I learned a lot about Pair Programming that I never knew by spending some quality time trying to get Copilot to help me produce useful code. I also learned some of the things it cannot do yet, but probably will eventually do, to help me write code faster, with far fewer issues.
It’s hard for me to quantify how much more productive I became as a Python programmer working with GitHub Copilot. What I can say is that I understand the differences between Python 3 and other scripting languages like Perl much better than I did before first using Copilot.
I also want to say that I am a much better software tester than I was before I started using Copilot. It is much easier to write error handling mechanisms in Python with its help. I also found two or three very specific contextual blindspots that Copilot has, that relate to using it to write code iteratively. I think I can test for those cases. And if those issues go away, I should realize that through my testing.
Where I’m Going from Here
I wrote imap-account-delete-from-sender.py to learn how to be productive with GitHub Copilot. This is a success, from my perspective.
Now I can move on to the work I want to do— refactoring and adding features to RinkAtlas.com, my North America ice hockey arena directory.
RinkAtlas is a much bigger project. The functionality I want to add is much more extensive and personally exciting to me.
How This Article Relates to My Other Writings
In my last article on this site, I wrote about What Apollo Program Success Can Teach Indy Developers in the 2020s. I analyzed a text from George M. Low written in 1970 and cited some key Apollo Project design principles that I intended to keep in mind when working on the next development cycle for RinkAtlas.
When I wrote that article, I knew what GitHub Copilot was, but had no idea if I would use it in the future.
Now that I’ve used Copilot I’d like to say that many of the points I made in What Apollo Program Success Can Teach Indy Developers in the 2020s still apply to my future work:
- I still want to “Minimize Functional Interfaces Between Complex Pieces” by leveraging third-party APIs. The value GitHub Copilot brings to my work is that it can help me be more productive when working with those APIs.
- “The Single Man Principle” is still really important. I have to fully understand my work and both sides of each interface to the third-party APIs that I’m using. Specifically, I still think switching from Angular.JS to Vue 3 is a good idea.
- I hope that by using GitHub Copilot and by simplifying the RinkAtlas software stack, I can better maintain “Control of Changes” as Dr. Low discussed.
Code Discussed in This Article is Available on GitHub
I made the Sys Admin Utilities repository and both the imap-account-size.py and imap-account-delete-from-sender.py scripts publicly available on GitHub.
See https://github.com/After6Services/sys-admin-utilities for details.
I Hope This Article Helped You
I hope that the examples I gave of things that GitHub Copilot could and could not tell me during the development of these simple Python scripts helps you understand why GitHub Copilot is not autopilot.
Footnotes
- L. Williams and R. R. Kessler, All I Really Needed to Know about Pair Programming I Learned in Kindergarten, (submitted to Communications of the ACM), Salt Lake City, UT, 1999. ↩︎
- L. Williams, R.R. Kessler, W. Cunningham, R. Jeffries, Strengthening the Case for Pair Programming, IEEE Software, New York, July / August 2000. ↩︎
- “What Doesn’t Kill Me Makes Me Stronger” is Aphorism Number 8 of Nietzsche’s “Maxims and Arrows” section of “Twilight of the Idols”. See https://en.wikipedia.org/wiki/Twilight_of_the_Idols. ↩︎