How to Use GitHub Copilot to Become More Productive Developer
Tags: github, beginners, webdev, react
Published: 2023-07-06 09:06:47

`<section class="post-content ">                      
<p>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.</p><p>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. </p><p>Weirdly, using Copilot has caused me to develop features faster than business stakeholders can review them.</p><p>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 <a href="https://github.blog/2022-09-07-research-quantifying-github-copilots-impact-on-developer-productivity-and-happiness/">substantially improved productivity</a>.</p><h2 id="how-to-setup-github-copilot">How to Setup GitHub Copilot</h2><p>For setup and basic usage of Copilot, check out the <a href="https://docs.github.com/en/copilot">docs</a>. You can add on Copilot to an individual or business account, and there's a free trial and reasonable <a href="https://github.com/features/copilot#pricing">pricing</a> after the trial. </p><p>After adding Copilot to your GitHub account, you'll want to install the <a href="https://docs.github.com/en/copilot/getting-started-with-github-copilot">plugins for your IDE</a> and log into GitHub to access Copilot.</p><p>In this article, we'll use these <a href="https://marketplace.visualstudio.com/search?term=copilot&amp;target=VSCode&amp;category=All%20categories&amp;sortBy=Relevance">Visual Studio Code extensions</a>:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>GitHub Extension</th>
<th>Description</th>
<th>Preview</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot">Copilot</a></td>
<td>AI pair programmer with in-IDE code suggestions</td>
<td>No</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-nightly">Copilot Nightly</a></td>
<td>Nightly build of Copilot, includes latest changes</td>
<td>No</td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-labs">Copilot Labs</a></td>
<td>Experimental features in sidebar</td>
<td><a href="https://githubnext.com/projects/copilot-labs/">Yes</a></td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat">Copilot Chat</a></td>
<td>Interactive chat in sidebar, part of Copilot X</td>
<td><a href="https://github.com/features/preview/copilot-x">Yes</a></td>
</tr>
<tr>
<td><a href="https://marketplace.visualstudio.com/items?itemName=GitHub.heygithub">Copilot Voice</a></td>
<td>Voice assistant</td>
<td><a href="https://githubnext.com/projects/copilot-voice/">Yes</a></td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>Notes:</p><ol><li>This info is up-to-date as the writing of this article, but will likely change as GitHub evolves these products.</li><li>I haven't yet received access to Copilot Voice or Copilot (X) for Pull Requests, so my perspective on these are limited and based on GitHub's preview docs.</li></ol><h3 id="privacy">Privacy</h3><p>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 <a href="https://github.com/features/copilot#faq">FAQ docs</a> and <a href="https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement">Privacy Statement</a>.]</p><h2 id="use-cases-for-github-copilot">Use Cases for GitHub Copilot</h2><p>The uses cases for GitHub Copilot are numerous, especially when you add in <a href="https://github.com/features/preview/copilot-x">t</a>he preview features of Labs, Chat, and Voice. Using Copilot's features can really streamline the development process. </p><p>Here are some great ways to leverage Copilot extensions:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Category</th>
<th>Extension(s)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Code Generation</td>
<td>Copilot, Copilot Nightly, Copilot Voice</td>
</tr>
<tr>
<td>Explaining Code</td>
<td>Copilot Labs, Copilot Chat, Copilot Voice</td>
</tr>
<tr>
<td>Language Translation</td>
<td>Copilot Labs, Copilot Chat</td>
</tr>
<tr>
<td>Debugging</td>
<td>Copilot Labs, Copilot Chat</td>
</tr>
<tr>
<td>Refactoring</td>
<td>Copilot Labs, Copilot Chat</td>
</tr>
<tr>
<td>Test Generation</td>
<td>Copilot, Copilot Nightly, Copilot Labs, Copilot Chat</td>
</tr>
<tr>
<td>Code Reviews</td>
<td>Copilot Chat</td>
</tr>
<tr>
<td>Voice-Driven Development</td>
<td>Copilot Voice</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><h3 id="code-generation">Code Generation</h3><p>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".</p><p>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.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screen-Recording-2023-06-09-at-8--1-.gif" class="kg-image" alt="Screen-Recording-2023-06-09-at-8--1-" width="826" height="266" loading="lazy"><figcaption>A comment prompt in Ruby that Copilot uses to create a method and invocation</figcaption></figure><p>Taking code completion one step further, Copilot Chat can take a single prompt and generate instructions for building an entire application. </p><p>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.</p><p>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.</p><h3 id="explaining-code">Explaining Code</h3><p>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.</p><p>Below is some code generated by Copilot Chat from my "REST API in Node..." prompt from above:</p><pre class="language-javascript" tabindex="0"><code class="language-javascript">app<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/products/:id'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> res</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> id <span class="token operator">=</span> req<span class="token punctuation">.</span>params<span class="token punctuation">.</span>id<span class="token punctuation">;</span>
  <span class="token keyword">const</span> product <span class="token operator">=</span> products<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">p</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> p<span class="token punctuation">.</span>id <span class="token operator">===</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>product<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span>product<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    res<span class="token punctuation">.</span><span class="token function">status</span><span class="token punctuation">(</span><span class="token number">404</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">error</span><span class="token operator">:</span> <span class="token string">'Product not found'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>I took this code and fed it into the "Explain" feature in Copilot Labs. Here's the response...</p><p><em>Here is the explanation for the code above: &nbsp;</em></p><ol><li><em>We define a route with the route path /products/:id. The colon : in the path indicates that it is a route parameter. The value of the route parameter will be available in req.params. For example, if the route path is /products/:id and we make a request to /products/5, then the value of req.params will be { id: 5 }.</em></li><li><em>We get the id from req.params and find the product with the given id from the products array. If a product with the given id exists, we return it. Otherwise, we send a 404 error response with the error message Product not found.</em></li></ol><p>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.</p><p>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:</p><pre><code>(?:[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&amp;'*+/=?^_`{|}~-]+)*|"(?:[\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]))\.){3}(?:(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])+)\])</code></pre><p>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. &nbsp;</p><h3 id="language-translation">Language Translation</h3><p>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:</p><pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">let</span> products <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'1'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Product 1'</span><span class="token punctuation">,</span> <span class="token literal-property property">price</span><span class="token operator">:</span> <span class="token number">10.99</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'2'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Product 2'</span><span class="token punctuation">,</span> <span class="token literal-property property">price</span><span class="token operator">:</span> <span class="token number">19.99</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'3'</span><span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'Product 3'</span><span class="token punctuation">,</span> <span class="token literal-property property">price</span><span class="token operator">:</span> <span class="token number">7.99</span> <span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre><pre class="language-ruby" tabindex="0"><code class="language-ruby">products <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span> <span class="token symbol">id</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'1'</span></span><span class="token punctuation">,</span> <span class="token symbol">name</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'Product 1'</span></span><span class="token punctuation">,</span> <span class="token symbol">price</span><span class="token operator">:</span> <span class="token number">10.99</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> <span class="token symbol">id</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'2'</span></span><span class="token punctuation">,</span> <span class="token symbol">name</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'Product 2'</span></span><span class="token punctuation">,</span> <span class="token symbol">price</span><span class="token operator">:</span> <span class="token number">19.99</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> <span class="token symbol">id</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'3'</span></span><span class="token punctuation">,</span> <span class="token symbol">name</span><span class="token operator">:</span> <span class="token string-literal"><span class="token string">'Product 3'</span></span><span class="token punctuation">,</span> <span class="token symbol">price</span><span class="token operator">:</span> <span class="token number">7.99</span> <span class="token punctuation">}</span>
<span class="token punctuation">]</span></code></pre><p>This is a very simple example, so let's try making this into a class and going the other way (from Ruby to JavaScript):</p><pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">class</span> <span class="token class-name">Product</span>
  attr_reader <span class="token symbol">:name</span><span class="token punctuation">,</span> <span class="token symbol">:price</span>

  <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">initialize</span></span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> price<span class="token punctuation">)</span>
    <span class="token variable">@name</span> <span class="token operator">=</span> name
    <span class="token variable">@price</span> <span class="token operator">=</span> price
  <span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre><pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">class</span> <span class="token class-name">Product</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> price</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> price<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre><p>This checks out. For good measure, I tested TypeScript as well, and it worked perfectly, too.</p><h3 id="debugging">Debugging</h3><p>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. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screen-Recording-2023-06-14-at-1.gif" class="kg-image" alt="Screen-Recording-2023-06-14-at-1" width="702" height="302" loading="lazy"><figcaption>Copilot Labs "Fix Bug" Brush Feature</figcaption></figure><p>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. </p><p>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:</p><pre class="language-javascript" tabindex="0"><code class="language-javascript">products<span class="token punctuation">[</span>productIndex<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">{</span> id<span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span>req<span class="token punctuation">.</span>body <span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre><p>I copied/pasted the error message in Chat, and it responded with...</p><blockquote>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 ".." before <code>req.body</code> is causing the error. It should be just one dot instead of two.</blockquote><p>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. </p><p>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.</p><h3 id="refactoring">Refactoring</h3><p>Another great use case is refactoring. Consider the code below:</p><pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">price_with_discount</span></span>
  price <span class="token operator">-</span> <span class="token punctuation">(</span>price <span class="token operator">*</span> discount_rate<span class="token punctuation">)</span>
<span class="token keyword">end</span></code></pre><p>This simple price calculation doesn't factor in <code>nil</code> values, strings that should be numbers, or reasonable values for <code>price</code> or <code>discount_rate</code>. We need the code to do this:</p><ol><li>Ensure <code>price</code> is an integer or float (not nil)</li><li>Ensure <code>price</code> is not negative</li><li>Ensure <code>discount_rate</code> is a float (not nil)</li><li>Ensure <code>discount_rate</code> is not negative and less than or equal to 1</li></ol><p>Copilot Chat handles this with ease...</p><pre class="language-ruby" tabindex="0"><code class="language-ruby"><span class="token keyword">def</span> <span class="token method-definition"><span class="token function">price_with_discount</span></span><span class="token punctuation">(</span>price<span class="token punctuation">,</span> discount_rate<span class="token punctuation">)</span>
  <span class="token keyword">raise</span> ArgumentError<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Price must be a positive number"</span></span> <span class="token keyword">unless</span> price<span class="token punctuation">.</span>is_a<span class="token operator">?</span><span class="token punctuation">(</span><span class="token builtin">Numeric</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> price <span class="token operator">&gt;=</span> <span class="token number">0</span>
  <span class="token keyword">raise</span> ArgumentError<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Discount rate must be a float between 0 and 1"</span></span> <span class="token keyword">unless</span> discount_rate<span class="token punctuation">.</span>is_a<span class="token operator">?</span><span class="token punctuation">(</span><span class="token builtin">Float</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> discount_rate <span class="token operator">&gt;=</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> discount_rate <span class="token operator">&lt;=</span> <span class="token number">1</span>

  price <span class="token operator">-</span> <span class="token punctuation">(</span>price <span class="token operator">*</span> discount_rate<span class="token punctuation">)</span>
<span class="token keyword">end</span></code></pre><h3 id="test-generation">Test Generation</h3><p>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.</p><p>Generating unit tests for code can be tedious if you're trying to cover happy and sad paths, primary use cases and edge cases. Our <code>price_with_discount</code> code above needs test cases to ensure an error is raised if <code>price</code> or <code>discount_rate</code> don't meet the requirements. In addition, we might want to test the following:</p><ol><li>A <code>price</code> without a <code>discount_rate</code></li><li>A <code>price</code> with a reasonable <code>discount_rate</code> (for example, a 10% discount)</li><li>A <code>price</code> with a 100% <code>discount_rate</code> (for example, if we want to give the product away)</li></ol><p>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.</p><pre class="language-ruby" tabindex="0"><code class="language-ruby">RSpec<span class="token punctuation">.</span>describe Product <span class="token keyword">do</span>
  describe <span class="token string-literal"><span class="token string">"#price_with_discount"</span></span> <span class="token keyword">do</span>
    context <span class="token string-literal"><span class="token string">"when given a valid price and discount rate"</span></span> <span class="token keyword">do</span>
      it <span class="token string-literal"><span class="token string">"returns the price with the discount applied"</span></span> <span class="token keyword">do</span>
        product <span class="token operator">=</span> <span class="token class-name">Product</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Test Product"</span></span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">0.2</span><span class="token punctuation">)</span>
        expect<span class="token punctuation">(</span>product<span class="token punctuation">.</span>price_with_discount<span class="token punctuation">(</span>product<span class="token punctuation">.</span>price<span class="token punctuation">,</span> product<span class="token punctuation">.</span>discount_rate<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>to eq<span class="token punctuation">(</span><span class="token number">80</span><span class="token punctuation">)</span>
      <span class="token keyword">end</span>
    <span class="token keyword">end</span>

    context <span class="token string-literal"><span class="token string">"when given a nil price"</span></span> <span class="token keyword">do</span>
      it <span class="token string-literal"><span class="token string">"raises an ArgumentError with an appropriate message"</span></span> <span class="token keyword">do</span>
        product <span class="token operator">=</span> <span class="token class-name">Product</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Test Product"</span></span><span class="token punctuation">,</span> <span class="token keyword">nil</span><span class="token punctuation">,</span> <span class="token number">0.2</span><span class="token punctuation">)</span>
        expect <span class="token punctuation">{</span> product<span class="token punctuation">.</span>price_with_discount<span class="token punctuation">(</span>product<span class="token punctuation">.</span>price<span class="token punctuation">,</span> product<span class="token punctuation">.</span>discount_rate<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">.</span>to raise_error<span class="token punctuation">(</span>ArgumentError<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Price must be a positive number"</span></span><span class="token punctuation">)</span>
      <span class="token keyword">end</span>
    <span class="token keyword">end</span>

    context <span class="token string-literal"><span class="token string">"when given a price less than 0"</span></span> <span class="token keyword">do</span>
      it <span class="token string-literal"><span class="token string">"raises an ArgumentError with an appropriate message"</span></span> <span class="token keyword">do</span>
        product <span class="token operator">=</span> <span class="token class-name">Product</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Test Product"</span></span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">0.2</span><span class="token punctuation">)</span>
        expect <span class="token punctuation">{</span> product<span class="token punctuation">.</span>price_with_discount<span class="token punctuation">(</span>product<span class="token punctuation">.</span>price<span class="token punctuation">,</span> product<span class="token punctuation">.</span>discount_rate<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">.</span>to raise_error<span class="token punctuation">(</span>ArgumentError<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Price must be a positive number"</span></span><span class="token punctuation">)</span>
      <span class="token keyword">end</span>
    <span class="token keyword">end</span>

    context <span class="token string-literal"><span class="token string">"when given a nil discount rate"</span></span> <span class="token keyword">do</span>
      it <span class="token string-literal"><span class="token string">"raises an ArgumentError with an appropriate message"</span></span> <span class="token keyword">do</span>
        product <span class="token operator">=</span> <span class="token class-name">Product</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Test Product"</span></span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token keyword">nil</span><span class="token punctuation">)</span>
        expect <span class="token punctuation">{</span> product<span class="token punctuation">.</span>price_with_discount<span class="token punctuation">(</span>product<span class="token punctuation">.</span>price<span class="token punctuation">,</span> product<span class="token punctuation">.</span>discount_rate<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">.</span>to raise_error<span class="token punctuation">(</span>ArgumentError<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Discount rate must be a float between 0 and 1"</span></span><span class="token punctuation">)</span>
      <span class="token keyword">end</span>
    <span class="token keyword">end</span>

    context <span class="token string-literal"><span class="token string">"when given a discount rate less than 0"</span></span> <span class="token keyword">do</span>
      it <span class="token string-literal"><span class="token string">"raises an ArgumentError with an appropriate message"</span></span> <span class="token keyword">do</span>
        product <span class="token operator">=</span> <span class="token class-name">Product</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Test Product"</span></span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">0.2</span><span class="token punctuation">)</span>
        expect <span class="token punctuation">{</span> product<span class="token punctuation">.</span>price_with_discount<span class="token punctuation">(</span>product<span class="token punctuation">.</span>price<span class="token punctuation">,</span> product<span class="token punctuation">.</span>discount_rate<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">.</span>to raise_error<span class="token punctuation">(</span>ArgumentError<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Discount rate must be a float between 0 and 1"</span></span><span class="token punctuation">)</span>
      <span class="token keyword">end</span>
    <span class="token keyword">end</span>

    context <span class="token string-literal"><span class="token string">"when given a discount rate greater than 1"</span></span> <span class="token keyword">do</span>
      it <span class="token string-literal"><span class="token string">"raises an ArgumentError with an appropriate message"</span></span> <span class="token keyword">do</span>
        product <span class="token operator">=</span> <span class="token class-name">Product</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span><span class="token string-literal"><span class="token string">"Test Product"</span></span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">1.5</span><span class="token punctuation">)</span>
        expect <span class="token punctuation">{</span> product<span class="token punctuation">.</span>price_with_discount<span class="token punctuation">(</span>product<span class="token punctuation">.</span>price<span class="token punctuation">,</span> product<span class="token punctuation">.</span>discount_rate<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">.</span>to raise_error<span class="token punctuation">(</span>ArgumentError<span class="token punctuation">,</span> <span class="token string-literal"><span class="token string">"Discount rate must be a float between 0 and 1"</span></span><span class="token punctuation">)</span>
      <span class="token keyword">end</span>
    <span class="token keyword">end</span>
  <span class="token keyword">end</span>
<span class="token keyword">end</span></code></pre><p>One test for valid input, five for invalid input. All six work, and I didn't have to write them!</p><h3 id="code-reviews">Code Reviews</h3><p>One feature of Copilot X is <a href="https://githubnext.com/projects/copilot-for-pull-requests#technical-preview-available-now">Copilot for Pull Requests</a>. Here are some of the key features:</p><ul><li><strong>Template Expansion</strong> – Leverage Copilot to fill in your PR template and explain code</li><li><strong>Gentest</strong> – Generate tests for your code based on Copilot's analysis</li><li><strong>Ghost Text</strong> – Receive suggestions while you're typing in the PR</li></ul><h3 id="voice-driven-development">Voice-Driven Development</h3><p>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:</p><ul><li><strong>Write/Edit Code</strong> – Use voice controls to trigger Copilot code suggestions</li><li><strong>Code Navigation</strong> – Navigate a file without a keyboard or mouse</li><li><strong>Control the IDE</strong> – Trigger any VS Code command</li><li><strong>Code Summarization</strong> – Get summaries of blocks of code</li></ul><h2 id="summary">Summary</h2><p>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.</p><p>Ignore click-bait promises of a "10x productivity gain", but don't ignore the research of <a href="https://github.blog/2022-09-07-research-quantifying-github-copilots-impact-on-developer-productivity-and-happiness/">Copilot's impact on developer productivity and happiness</a>. </p><p>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.</p>
</section>`