Logo
blank Skip to main content

GitHub Copilot Review: How to Accelerate Your Software Development

The speed of software development can be as important as the quality of your code. By releasing great products faster than competitors, businesses have a better chance to overcome the competition and attract users.

GitHub Copilot is an AI-based tool that helps developers automate coding tasks and improve code quality. By analyzing entire projects, it suggests intelligent completions and generates functions, documentation, and even tests, helping your team save valuable time. 

According to GitHub, Copilot can accelerate coding by up to 55%. However, Copilotโ€™s effectiveness depends on various factors, including your teamโ€™s skill level and your projectโ€™s limitations. We decided to test Copilotโ€™s efficiency and discovered that for our (usually complex) projects, GitHub Copilot shortens development cycles by approximately 20%.

In this article, our development specialists provide a detailed GitHub Copilot review that covers key use cases and practical examples. This article will be useful for product managers and development leaders seeking ways to deliver high-quality products faster while maintaining control and security.

What is GitHub Copilot?

GitHub Copilot is an AI-based development tool that assists software developers with coding tasks. Your development team can use it to do the following:

  • Generate code
  • Answer code-related questions
  • Create explanations of commands
  • Write pull request descriptions
  • And more

GitHub Copilot is becoming increasingly popular in the development community due to its convenience and functionality. However, by helping developers, this tool brings value to businesses too.

Business benefits of GitHub Copilot

GitHub Copilot has many technical benefits, and your business can leverage it to help your business succeed.

benefits of GitHub Copilot
  • Accelerates time to market. GitHub Copilot shortens development cycles, helping businesses get their products to market faster. By streamlining coding, debugging, and testing, it enables quicker launches without compromising on quality.
  • Reduces development costs. By speeding up coding, testing, and troubleshooting, GitHub Copilot lowers the overall cost of software development. It automates routine tasks, allowing developers to focus on higher-value work, resulting in more efficient resource use and a faster return on investment.
  • Enhances productivity. Copilot enables faster task completion by providing automated code suggestions, documentation, and test creation. Additionally, it integrates with popular code editors like Visual Studio Code, Neovim, and JetBrains Fleet. This allows businesses to enhance their existing development environments without needing to switch tools or workflows. 
  • Improves code quality. By generating clean, reliable code with comprehensive documentation, GitHub Copilot reduces the risk of errors and makes the codebase easier to maintain. This leads to higher-quality products that require fewer fixes and are easier to scale.
  • Improves security. GitHub Copilot helps developers follow secure coding practices by suggesting code that incorporates proper input validation, secure authentication mechanisms, and defenses against vulnerabilities like SQL and path injections. It may also recommend updated cryptographic algorithms, ensuring the use of modern and secure standards. Furthermore, Copilot can help prevent common security issues such as hardcoding credentials or mishandling sensitive data, reducing the likelihood of introducing vulnerabilities.
  • Minimizes external dependencies. Developers can resolve issues internally using the integrated chat feature within IDEs without relying on external resources like Google or Stack Overflow. This saves time, reduces distractions, and keeps projects moving smoothly.
  • Simplifies onboarding. Copilot can assist developers in quickly understanding legacy code, reducing the time and effort required to onboard new team members. This is especially valuable in large and complex projects where getting up to speed can take considerable time for new developers.

Note: Despite the numerous advantages of GitHub Copilot for software development, we respect our clientsโ€™ concerns and policies and only use it when explicitly approved by a client. Information covered by an NDA is never shared with GitHub Copilot. 

Now, letโ€™s explore the main use cases of GitHub Copilot.

Speed up your time to market without sacrificing quality

Hire an experienced team of developers who use AI responsibly and deliver high-quality products fast.

Top GitHub Copilot use cases 

GitHub Copilot is a versatile tool that can be used for a range of software development-related tasks. Although itโ€™s not omnipotent and still requires the eye of an experienced developer, it can help your team with: 

  • Generating or completing code. GitHub Copilot can suggest whole functions, methods, or code snippets based on the context of your project. It helps developers write code faster by completing partial lines or generating entire blocks of code that fit within the structure of the existing codebase.
  • Debugging. Copilot can help your team identify bugs by providing suggestions for fixing errors or improving problematic code. 
  • Writing documentation. Copilot automates the generation of code comments and documentation, helping developers maintain clarity and readability. This is especially useful for creating consistent, professional documentation while minimizing manual effort.
  • Creating tests. GitHub Copilot helps to write unit tests by suggesting relevant test cases for your code, ensuring higher test coverage and improved software reliability. 
  • Legacy code support. When working with legacy code, Copilot can provide your team with explanations and insights into how the code works. This reduces the ramp-up time for new developers and simplifies ongoing maintenance.

Now, letโ€™s demonstrate how to use GitHub Copilot effectively using practical examples from our development experience.

How to use Copilot effectively: practical examples

At Apriorit, we use an internally trained implementation of GitHub Copilot with our clientsโ€™ approval. In this section, weโ€™ll show the most common ways we leverage this tool to deliver better code faster.

Automating repetitive tasks

One of the most useful advantages of GitHub Copilot for developers is its ability to handle small and simple yet time-consuming tasks. Your development team can delegate tasks related to testing and prototyping and save time for solving more complex issues.

Interface mocking

Mock interfaces are simplified software components that simulate the behavior of real systems. Theyโ€™re important for testing because they allow developers to verify interactions between components without fully developing them with real dependencies. However, creating mock interfaces can be annoying and time-consuming. 

GitHub Copilot can speed up this process and allow developers to focus on implementing core functionality rather than generating repetitive mock data. For instance, here is the interface of ICalculator in C++

C++
class ICalculator 
{ 
public: 
	virtual ~ICalculator() = default; 
 
	virtual double Add(double a, double b) const = 0; 
	virtual double Subtract(double a, double b) const = 0; 
	virtual double Multiply(double a, double b) const = 0; 
	virtual double Divide(double a, double b) const = 0; 
}; 

Letโ€™s give Copilot this prompt: 

> generate mock using old style of gmock for ICalculator 

The result is the following:

C++
class MockCalculator : public ICalculator 
{ 
public: 
	MOCK_CONST_METHOD2(Add, double(double a, double b)); 
	MOCK_CONST_METHOD2(Subtract, double(double a, double b)); 
	MOCK_CONST_METHOD2(Multiply, double(double a, double b)); 
	MOCK_CONST_METHOD2(Divide, double(double a, double b)); 
}; 

Done! A task that would take a developer several minutes took Copilot only one second. 

Generating unit tests 

Your team can use Copilot to generate unit tests for code, as it:

  • Saves a lot of time
  • Reduces errors in test writing 
  • Provides a starting point for complex testing scenarios 

Letโ€™s look at an example using our implementation of Calculator.

C++
class Calculator : public ICalculator { 
public: 
	double Add(double a, double b) const override { 
		return a + b; 
	} 
	double Subtract(double a, double b) const override { 
		return a - b; 
	} 
	double Multiply(double a, double b) const override { 
		return a * b; 
	} 
	double Divide(double a, double b) const override { 
		if (b == 0) { 
			throw std::invalid_argument("Division by zero"); 
		} 
		return a / b; 
	} 
}; 

Now, we will try this prompt in Copilot: 

> generate unit tests for Calculator 

Hereโ€™s the result we receive:

C++
#include <gtest/gtest.h> 
#include "Calculator.h" 
 
class CalculatorTest : public ::testing::Test { 
protected: 
	Calculator calculator; 
}; 
 
TEST_F(CalculatorTest, Add) { 
	EXPECT_DOUBLE_EQ(calculator.Add(1.0, 2.0), 3.0); 
	EXPECT_DOUBLE_EQ(calculator.Add(-1.0, -2.0), -3.0); 
} 
 
TEST_F(CalculatorTest, Subtract) { 
	EXPECT_DOUBLE_EQ(calculator.Subtract(5.0, 3.0), 2.0); 
	EXPECT_DOUBLE_EQ(calculator.Subtract(-5.0, -3.0), -2.0); 
} 
 
TEST_F(CalculatorTest, Multiply) { 
	EXPECT_DOUBLE_EQ(calculator.Multiply(2.0, 3.0), 6.0); 
	EXPECT_DOUBLE_EQ(calculator.Multiply(-2.0, -3.0), 6.0); 
} 
 
TEST_F(CalculatorTest, Divide) { 
	EXPECT_DOUBLE_EQ(calculator.Divide(6.0, 3.0), 2.0); 
	EXPECT_THROW(calculator.Divide(1.0, 0.0), std::invalid_argument); 
} 

Again, it takes about five seconds to prepare, type the prompt, and generate the result, as opposed to minutes spent coming up with the tests and typing them out. 

Read also

Benefits and Risks of Outsourcing Engineering Services

Optimize costs, enhance expertise, and accelerate project timelines. Make the most of IT outsourcing by knowing what advantages to expect and how to address common concerns.

Learn more
blog-181-article-var2.jpg

Generating commit messages

Often, developers use generic commit messages like โ€œfixโ€ or โ€œupdate.โ€ This is problematic because they lack context about the changes made, making it difficult for team members to quickly understand the purpose of a commit. Without clear commit messages, it becomes harder to track the history of changes, especially when reviewing a project or debugging issues later. 

Letโ€™s use the prompt โ€œgenerate commit message for the patch belowโ€ and insert the patch. As a result, GitHub Copilot generates the following commit message: 

add unit tests for Calculator class 

- Added unit tests for all methods using Google Test framework
- Included edge case handling for division by zero 

Copilot generated a clear commit message based on the changes developers made. This is valuable, as it helps to clearly document the projectโ€™s historical evolution and allows team members to quickly understand each commitโ€™s purpose. 

Enhancing code quality and efficiency 

Copilot can play the role of code reviewer and help your team quickly check their code or figure out what went wrong. Letโ€™s see how it achieves this using the examples below.

Optimizing code

Sometimes, our developers work with legacy projects where existing code is inefficient. For example, the code might rely on loops or custom functions instead of built-in library functions. These older coding practices can make the project hard to maintain, slow to execute, and difficult to scale. GitHub Copilot can help to improve the code by suggesting algorithms from standard libraries. This allows developers to mitigate potential performance issues and enhance code readability. 

For instance, hereโ€™s a piece of legacy code that could use some improvement as it contains a loop. 

C++
std::vector<std::string> data_str = {...}; 
std::vector<int32_t> data_int = {...}; 
 
for (size_t i = 0; i < data_int.size(); ++i) 
	data_str.push_back(std::to_string(data_int[i])); 

While functional, this loop can be replaced with a more concise and efficient solution from the standard library. Using standard library algorithms often provides better performance and clarity, as they are specifically designed for container processing.

Letโ€™s use the prompt โ€œimprove the codeโ€ to get the following optimized solution:

C++
// Assuming data_str and data_int are initialized with some values 
std::vector<std::string> data_str = {...}; 
std::vector<int32_t> data_int = {...}; 
 
// Reserve space to avoid multiple reallocations 
data_str.reserve(data_str.size() + data_int.size()); 
 
// Use std::transform to convert and append integers to strings 
std::transform(data_int.begin(), data_int.end(),                                    
std::back_inserter(data_str), [](const auto& item) { 
	return std::to_string(item); 
}); 

In this improved version, we:

  1. Reserve space in data_str to avoid multiple reallocations.
  2. Use std::transform to convert and append integers to strings in a single, more efficient operation.

Note that GitHub Copilot automatically suggests and explains these improvements for better readability and performance.

Handling code and compilation errors

When developers encounter a compilation error or a function that doesnโ€™t work as expected, they typically turn to resources like Google or Stack Overflow to find solutions. However, with GitHub Copilot, this isnโ€™t necessary. 

GitHub Copilot serves a similar purpose as those resources but has the advantage of being fully integrated into the project. Thanks to this immersion in the project context, it can offer precise and effective solutions to errors. 

Querying unknown concepts 

Developers frequently encounter non-standard programming concepts and syntax. Copilot can quickly explain a concept, allowing developers to spend more time coding and discussing architecture and less time searching for answers. 

However, Copilot canโ€™t replace learning the art of programming; responses can contain inaccuracies, so itโ€™s important to verify them. Besides this, the more accurate and detailed the prompt, the higher the chances of receiving a working result. 

Read also

Writing Clear Functional and Non-functional Requirements: Tips and Best Practices from Apriorit Experts

Ensure alignment and clarity throughout the development process by articulating both functional and non-functional requirements. Read our guide for useful tips on writing them.

Learn more
functional and non-functional requirements

Generating code using requirements

GitHub Copilot can process detailed requirements provided by a client or project manager. If these requirements involve changes or additions to the code, Copilot can implement them, saving developers a significant amount of time.

However, if the requirements refer to an external project, the generated code may be inaccurate or incomplete.

To test this ability, we sent 10 prompts with โ€œrequirements as isโ€ to Copilot. Nine were successful, while one failed due to dependencies on multiple projects that GitHub Copilot couldnโ€™t access.

For example, consider the ICalculator interface, which includes four standard operations (addition, subtraction, multiplication, and division). This interface is part of a library used by the client. Imagine the client sends additional requirements, such as:

  1. Square operation. Implement a method to calculate the square of a double value.
    Method signature: double square(double a) const
  2. Sine operation. Implement a method to calculate the sine of a double value (in radians).
    Method signature: double sin(double a) const
  3. Cosine operation
    Implement a method to calculate the cosine of a double value (in radians).
    Method signature: double cos(double a) const

Letโ€™s use a detailed prompt asking Copilot to update the ICalculator interface and the Calculator implementation. 

First, Copilot updates the interface and implementation:

C++
class ICalculator { 
public: 
	// ... previous code is hidden 
	virtual double square(double a) const = 0; 
	virtual double sin(double a) const = 0; 
	virtual double cos(double a) const = 0; 
}; 
#include <cmath>  
 
class Calculator : public ICalculator { 
public: 
	// ... previous code is hidden 
	double Square(double a) const override { 
		return a * a; 
	} 
 
	double Sin(double a) const override { 
		return std::sin(a); 
	} 
 
	double Cos(double a) const override { 
		return std::cos(a); 
	} 
}; 

Then, Copilot generates unit tests to verify the functionality of the Calculator class. Each test ensures that the new methods (Square, Sin, and Cos) perform as expected:

C++
// previous tests are hidden 
TEST(CalculatorTest, Square) { 
	EXPECT_DOUBLE_EQ(calculator.Square(5), 25); 
	EXPECT_DOUBLE_EQ(calculator.Square(-4), 16); 
} 
 
TEST(CalculatorTest, Sin) { 
	EXPECT_NEAR(calculator.Sin(3.14159 / 2), 1.0, 1e-5); // Approx. ฯ€/2 radians 
	EXPECT_NEAR(calculator.Sin(0), 0.0, 1e-5); 
} 
 
TEST(CalculatorTest, Cos) { 
	EXPECT_NEAR(calculator.Cos(0), 1.0, 1e-5); 
	EXPECT_NEAR(calculator.Cos(3.14159), -1.0, 1e-5); // Approx. ฯ€ radians 
} 

As you can see, Copilot can easily generate these additions, saving developers time and effort. However, this example is rather simple; for more complex tasks, you need an expert team that can verify the Copilot output.

Related project

Building an AI-based Healthcare Solution

Discover how Apriorit accelerated and simplified doctorsโ€™ routine tasks by building an AI-based system. Our client got an efficient solution that can track, detect, and measure ovarian follicles in video footage with astonishing accuracy.

Project details
Building an AI-based Healthcare Solution

What to consider when using Copilot for software development

Despite its advantages, Copilot isnโ€™t perfect โ€” it still requires attention and oversight from experienced developers. When you decide to use Copilot in your project, your team should be aware of the following limitations: 

  1. Limited work with complex code. GitHub Copilot struggles to generate accurate tests for complex code, particularly when multiple classes interact with a main class. As a result, generated tests may not work correctly, requiring developers to manually adjust the test implementation. However, Copilot is useful for naming unit tests and creating a foundation for reusable tests.
  2. No access to external libraries or projects. Copilot can analyze internal project code but lacks access to external libraries or projects. This limitation can affect the accuracy and completeness of generated code or tests. 
  3. Limited quality of generated code. The quality of generated code doesnโ€™t always meet expectations, and code may occasionally fail to function. Developers often need to refine prompts or regenerate code for better results.

Letโ€™s demonstrate how Copilot can make mistakes in code. Consider a case where a developer has two vectors:

C++
std::vector<std::string> data_str = {...}; 
std::vector<int32_t> data_int = {...}; 

We ask Copilot to create a map pairing strings with integers using std::transform in C++17, and hereโ€™s the result we receive:

C++
std::vector<std::string> data_str = {"key0", "key1", "key2"}; 
std::vector<int32_t> data_int = {10, 20, 30}; 
 
std::map<std::string, int32_t> data_map; 
 
std::transform(data_str.begin(), data_str.end(), data_int.begin(), 
            	std::inserter(data_map, data_map.end()), 
            	{ 
                		return std::make_pair(key, value); 
            	}); 
 
// Print the map to verify 
for (const auto& pair : data_map) { 
	std::cout << pair.first << ": " << pair.second << std::endl; 
}

The problem is that the generated code is incorrect because of an issue in the final parameter of std::transform. The corrected code should use a lambda with explicit parameters:

C++
std::transform(data_str.begin(), data_str.end(), data_int.begin(), 
           	std::inserter(data_map, data_map.end()), [](const auto& key, const auto& value) 
            	{ 
                		return std::make_pair(key, value); 
            	});

Alternatively, std::pair can replace std::make_pair for improved clarity. While Copilot provides a starting point, developers must often debug and refine the generated code to ensure it works as intended.

Conclusion  

Accelerating development without sacrificing quality has always been a holy grail for businesses and software developers. With GitHub Copilot, it has finally become possible. 

While Copilot canโ€™t replace a team of professional developers, it helps engineers bypass routine coding tasks and focus on complex, value-driven solutions for clients. By using Copilotโ€™s automation capabilities, our team improves efficiency and delivers high-quality results in a shorter time.

If youโ€™d like to complete your projects faster, hire a dedicated Apriorit team for expert software development services. With your consent, we will use the latest tools to push your product to the market faster and help you get ahead of your competition.

Looking for a team of developers with rare skills?

We do what AI isnโ€™t capable of. From reverse engineering to kernel development, we are ready to tackle any task regardless of its complexity.

Have a question?

Ask our expert!

Olya-Kolomoets
Olya Kolomoets

R&D Delivery Manager

Tell us about
your project

...And our team will:

  • Process your request within 1-2 business days.
  • Get back to you with an offer based on your project's scope and requirements.
  • Set a call to discuss your future project in detail and finalize the offer.
  • Sign a contract with you to start working on your project.

Do not have any specific task for us in mind but our skills seem interesting? Get a quick Apriorit intro to better understand our team capabilities.