1
0
mirror of synced 2025-12-21 19:06:49 -05:00

[DO NOT MERGE] Official megabranch of "Copilot Chat Cookbook" with new map topic structure (#53046)

Co-authored-by: hubwriter <hubwriter@github.com>
Co-authored-by: Ben Ahmady <32935794+subatoi@users.noreply.github.com>
Co-authored-by: Tiago Pascoal <tspascoal@github.com>
Co-authored-by: Christopher Harrison <geektrainer@github.com>
Co-authored-by: Sophie <29382425+sophietheking@users.noreply.github.com>
Co-authored-by: Sunbrye Ly <56200261+sunbrye@users.noreply.github.com>
Co-authored-by: sunbrye <sunbrye@github.com>
Co-authored-by: Hector Alfaro <hectorsector@github.com>
Co-authored-by: Siara <108543037+SiaraMist@users.noreply.github.com>
This commit is contained in:
Sarita Iyer
2024-11-22 15:31:27 -05:00
committed by GitHub
parent 286327d5db
commit 262ab79a12
34 changed files with 1999 additions and 22 deletions

View File

@@ -0,0 +1,149 @@
---
title: Decoupling business logic from UI components
shortTitle: Decoupling business logic
intro: '{% data variables.product.prodname_copilot_chat_short %} can help you separate your business logic from your user interface code, making it easier to maintain and scale your application.'
versions:
feature: copilot
category:
- 'Refactoring code'
complexity:
- Advanced
octicon: rocket
topics:
- Copilot
---
In many front-end frameworks, business logic can become embedded within user interface (UI) components, making the code hard to maintain or extend, especially as the application grows.
{% data variables.product.prodname_copilot_chat_short %} can help you refactor your code to extract business logic into separate services or state management solutions. This allows the business logic to be reused across different UI components. It also makes the code easier to scale by separating concerns and reducing interdependencies, and it simplifies unit testing by allowing business logic to be tested independently from UI components.
## Example scenario
In this JavaScript code, the `Counter` component configures the business logic for the component and also defines UI code for using the component in a web page.
```javascript
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
if (count >= 10) {
alert('Count cannot exceed 10');
return;
}
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
export default Counter;
```
## Example prompt
`Use Redux to separate the UI code from the Counter component in this code.`
## Example response
{% data variables.product.prodname_copilot_short %} provides step-by-step instructions for refactoring the code.
The suggested changes refactor the `Counter` component to use Redux for state management. This includes setting up a Redux store with an initial state and a reducer, defining an increment action, and modifying the `Counter` component to connect to the Redux store. Finally, the application is wrapped with a `Provider` component to make the Redux store available to the `Counter` component. This separates the UI logic from state management.
The proposed changes separate the code into five separate files.
> [!NOTE] {% data variables.product.prodname_copilot_chat_short %} responses are non-deterministic, so you may get a different response from the one described here.
### actions.js
```javascript
export const increment = () => ({
type: 'INCREMENT'
});
```
### reducer.js
```javascript
const initialState = {
count: 0
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
if (state.count >= 10) {
alert('Count cannot exceed 10');
return state;
}
return {
...state,
count: state.count + 1
};
default:
return state;
}
};
export default counterReducer;
```
### store.js
```javascript
import { createStore } from 'redux';
import counterReducer from './reducer';
const store = createStore(counterReducer);
export default store;
```
### counter.js
```javascript
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './actions';
const Counter = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
</div>
);
};
export default Counter;
```
### index.js
```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './counter';
ReactDOM.render(
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
);
```
## Further reading
{% data reusables.copilot.example-prompts.further-reading-items %}

View File

@@ -0,0 +1,67 @@
---
title: Fixing lint errors
shortTitle: Lint errors
intro: '{% data variables.product.prodname_copilot_chat_short %} can suggest ways to fix issues identified by a code linter.'
versions:
feature: copilot
category:
- 'Refactoring code'
complexity:
- Intermediate
octicon: rocket
topics:
- Copilot
---
It's good practice to use a linter to check your code for potential errors, style violations, or deviations from best practices. Linters can help you to catch bugs early, improve the readability of your code, and ensure that your code is consistent and maintainable.
## Example scenario
You have run a linter on your code and it has identified some issues that need to be fixed. Rather than fixing these manually, you can ask {% data variables.product.prodname_copilot_chat_short %} to fix them for you.
## Example prompts
* Select all of the code in the editor, then type:
`Fix the lint errors`
* You can specify a particular set of coding guidelines for a language, such as PEP8 for Python:
`Use PEP8 to fix the lint errors`
* If you have a local file that defines your coding conventions and rules, you can drag the file into the chat window to add it as an attachment, then type:
`Use the attached style guide to fix the lint errors`
* Alternatively, you can ask {% data variables.product.prodname_copilot_chat_short %} to fix only a specific type of lint error:
`Make sure all functions use snake_case naming style`
## Example response
{% data variables.product.prodname_copilot_short %} tells you what needs to be changed, and then gives you the corrected code. You should review the suggested code thoroughly before using it. The code that {% data variables.product.prodname_copilot_short %} suggests may not fix all of the issues identified by your linter, so you should always run the linter again if you choose to use the suggested code.
Linting issues that {% data variables.product.prodname_copilot_short %} can help you fix include:
* Adding necessary imports that are missing.
* Removing imports that are not used in the code.
* Splitting import statements into separate lines.
* Using method and function names that follow style guidelines.
* Adding spaces around operators.
* Ensuring consistent indentation.
* Removing trailing whitespace.
* Splitting multiple statements that are on a single line into separate lines.
* Breaking long line into multiple lines.
* Removing unused variables.
* Adding or removing blank lines to adhere to style guidelines.
* Adding docstrings to functions, classes, and modules.
* Removing code that will never be executed.
* Ensuring that all return statements in a function either return a value or none.
* Reducing or eliminating the use of global variables.
* Ensuring that functions are called with the correct number and type of arguments.
* Ensuring that comments are placed correctly and are meaningful.
* Replacing print statements with proper logging.
## Further reading
{% data reusables.copilot.example-prompts.further-reading-items %}

View File

@@ -0,0 +1,275 @@
---
title: 'Improving code readability and maintainability'
shortTitle: Improve code readability
intro: '{% data variables.product.prodname_copilot_chat_short %} can suggest ways to make your code easier to understand and maintain.'
versions:
feature: copilot
category:
- 'Refactoring code'
complexity:
- Simple
octicon: rocket
topics:
- Copilot
---
Code with poor readability is difficult for other developers to maintain and extend. {% data variables.product.prodname_copilot_chat_short %} can help in a number of ways. For example, by:
* [Suggesting improvements to variable names](#improving-variable-names)
* [Avoiding sequential conditional checks](#avoiding-sequential-conditional-checks)
* [Reducing nested logic](#reducing-nested-logic)
* [Splitting large methods into smaller, more readable ones](#splitting-up-large-methods)
Documenting your code is another way to improve the maintainability of your code. For information about using {% data variables.product.prodname_copilot_chat_short %} to help you add useful comments to your code, see the example prompts in "[Documenting code](/copilot/example-prompts-for-github-copilot-chat/documenting-code)."
> [!NOTE] The responses shown in this article are examples. {% data variables.product.prodname_copilot_chat_short %} responses are non-deterministic, so you may get different responses from the ones shown here.
## Improving variable names
Descriptive variable names and parameter names make it easier to understand their purpose.
### Example scenario
This JavaScript code logs a message about a person's age to the console. The abstract parameter names make it difficult to understand the purpose of the code.
```javascript
function logPersonsAge(a, b, c) {
if (c) {
console.log(a + " is " + b + " years old.");
} else {
console.log(a + " does not want to reveal their age.");
}
}
```
### Example prompt
In the editor, select the function you want to change, then ask {% data variables.product.prodname_copilot_chat_short %}:
`Improve the variable names in this function`
### Example response
{% data variables.product.prodname_copilot_short %} suggests descriptive variable names.
```javascript
function logPersonAge(name, age, revealAge) {
if (revealAge) {
console.log(name + " is " + age + " years old.");
} else {
console.log(name + " does not want to reveal their age.");
}
}
```
## Avoiding sequential conditional checks
`if...else` chains can be difficult to read, especially when they are long.
### Example scenario
This Python code prints the sound that various animals make, if defined, or "Unknown animal" if the animal type is not recognized. However, the chain of `if...else` statements makes the code inefficient and cumbersome.
```python
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class Bird(Animal):
def speak(self):
return "Tweet!"
def animal_sound(animal_type):
if animal_type == "dog":
return Dog().speak()
elif animal_type == "cat":
return Cat().speak()
elif animal_type == "bird":
return Bird().speak()
else:
return "Unknown animal"
print(animal_sound("dog"))
print(animal_sound("cat"))
print(animal_sound("bird"))
print(animal_sound("fish"))
```
### Example prompt
`Simplify this code. Avoid using if/else chains but retain all function return values.`
### Example response
{% data variables.product.prodname_copilot_short %} suggests using a dictionary to map the animal types to their corresponding classes.
```python
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
class Bird(Animal):
def speak(self):
return "Tweet!"
def animal_sound(animal_type):
animals = {
"dog": Dog,
"cat": Cat,
"bird": Bird
}
animal_class = animals.get(animal_type, Animal)
return animal_class().speak() if animal_class != Animal else "Unknown animal"
print(animal_sound("dog"))
print(animal_sound("cat"))
print(animal_sound("bird"))
print(animal_sound("fish"))
```
## Reducing nested logic
Deeply nested structures can make the code hard to follow, making it difficult to modify or extend the logic of the code in future.
### Example scenario
This Ruby code prints information about a user account based on three parameters. The use of nested `if...else` statements makes the code unnecessarily complex.
```ruby
def determine_access(user_role, has_permission, is_active)
if user_role == "admin"
if has_permission
if is_active
"Active admin account with full access."
else
"Inactive admin account."
end
else
"Admin account lacks necessary permissions."
end
else
"Access denied."
end
end
puts determine_access("admin", true, true)
puts determine_access("admin", true, false)
puts determine_access("admin", false, true)
puts determine_access("user", true, true)
```
### Example prompt
`Rewrite this code to avoid the nested if/else statements`
### Example response
{% data variables.product.prodname_copilot_short %} suggests using guard clauses to handle the conditions early and return the appropriate messages.
```ruby
def determine_access(user_role, has_permission, is_active)
return "Access denied." unless user_role == "admin"
return "Admin account lacks necessary permissions." unless has_permission
return "Inactive admin account." unless is_active
"Active admin account with full access."
end
puts determine_access("admin", true, true)
puts determine_access("admin", true, false)
puts determine_access("admin", false, true)
puts determine_access("user", true, true)
```
## Splitting up large methods
It can be difficult to grasp exactly what a method or function does if it is too long, making it difficult to maintain. Methods or functions that perform multiple tasks may not be reusable in other contexts. It may also be difficult to test each task in isolation.
### Example scenario
This Java method processes a customer order and prints a message. It performs multiple tasks in a single method.
```java
public void processOrder(Order order) {
if (order == null || order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order is invalid.");
}
double totalPrice = 0.0;
for (Item item : order.getItems()) {
totalPrice += item.getPrice() * item.getQuantity();
}
order.setTotalPrice(totalPrice);
if (totalPrice > 0) {
order.setStatus("Processed");
} else {
order.setStatus("Pending");
}
System.out.println("Order for customer " + order.getCustomerName() + " has been processed. Total price: " + totalPrice);
}
```
### Example prompt
`How could the processOrder method be refactored to be more useful and easier to maintain`
### Example response
{% data variables.product.prodname_copilot_short %} suggests breaking this method down into smaller, more focused methods.
```java
public void processOrder(Order order) {
validateOrder(order);
double totalPrice = calculateTotalPrice(order);
updateOrderStatus(order, totalPrice);
printOrderSummary(order, totalPrice);
}
private void validateOrder(Order order) {
if (order == null || order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order is invalid.");
}
}
private double calculateTotalPrice(Order order) {
double totalPrice = 0.0;
for (Item item : order.getItems()) {
totalPrice += item.getPrice() * item.getQuantity();
}
order.setTotalPrice(totalPrice);
return totalPrice;
}
private void updateOrderStatus(Order order, double totalPrice) {
if (totalPrice > 0) {
order.setStatus("Processed");
} else {
order.setStatus("Pending");
}
}
private void printOrderSummary(Order order, double totalPrice) {
System.out.println("Order for customer " + order.getCustomerName() + " has been processed. Total price: " + totalPrice);
}
```
## Further reading
{% data reusables.copilot.example-prompts.further-reading-items %}

View File

@@ -0,0 +1,15 @@
---
title: Refactoring code
intro: 'Discover ways that you can use {% data variables.product.prodname_copilot %} to refactor your code.'
versions:
feature: copilot
topics:
- Copilot
children:
- /refactoring-data-access-layers
- /improving-code-readability-and-maintainability
- /decoupling-business-logic-from-ui-components
- /fixing-lint-errors
- /refactoring-to-implement-a-design-pattern
- /refactoring-for-performance-optimization
---

View File

@@ -0,0 +1,127 @@
---
title: Refactoring data access layers
shortTitle: Data access layers
intro: '{% data variables.product.prodname_copilot_chat_short %} can suggest ways to decouple your data access code from your business logic, making an application easier to maintain and scale.'
versions:
feature: copilot
category:
- 'Refactoring code'
complexity:
- Advanced
octicon: rocket
topics:
- Copilot
---
Hardcoded SQL queries and tightly coupled data access code can make it difficult to scale or switch databases, and they often lead to repeated logic.
{% data variables.product.prodname_copilot_chat_short %} can recommend implementing a repository pattern or data access layer that abstracts database interactions, making the code more modular and reducing redundancy. It can also help refactor SQL queries into optimized versions, improving performance and maintainability.
> [!NOTE] The responses described in this article are examples. {% data variables.product.prodname_copilot_chat_short %} responses are non-deterministic, so you may get different responses from the ones shown here.
## Example scenario
This Python code connects to a SQLite database, retrieves a user record, and returns the user data. However, it fails to abstract the database connection logic and uses a hardcoded query that's vulnerable to SQL injection.
```python
import sqlite3
def get_user_by_id(user_id):
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
cursor.execute(f"SELECT display_name FROM users WHERE id = {user_id}")
user = cursor.fetchone()
conn.close()
return user
```
## Example prompt 1
You can start by asking {% data variables.product.prodname_copilot_short %} a general question about how to improve the code.
`How can I improve this code to make it safe and easier to update and expand? List possible improvements but don't show revised code.`
## Example response 1
{% data variables.product.prodname_copilot_short %} makes several suggestions for improving the code:
* Abstract the database connection details.
* Use parameterized queries to prevent SQL injection.
* Use a context manager to ensure that the database connection is properly closed if an error occurs.
* Add error handling.
## Example prompt 2
You can use the response to your first prompt to write a more specific prompt.
`Rewrite this code to make it more scalable and easier to maintain. Use a context manager. Avoid hardcoded SQL queries and tightly coupled data access code. Instead, use a repository pattern to abstract database interactions and make the code more modular and reusable. Where possible optimize the code to improve performance. Include error trapping, and make sure the code is not vulnerable to SQL injection.`
## Example response 2
{% data variables.product.prodname_copilot_short %} suggests splitting the code into separate files, each with a specific purpose. It also provides example code for returning a user's details.
The suggested changes implement a repository pattern and use parameterized queries to avoid SQL injection.
```python
# database.py
import sqlite3
class Database:
def __init__(self, db_path):
self.db_path = db_path
def __enter__(self):
self.conn = sqlite3.connect(self.db_path)
self.cursor = self.conn.cursor()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type or exc_val or exc_tb:
self.conn.rollback()
else:
self.conn.commit()
self.conn.close()
# user_repository.py
class UserRepository:
def __init__(self, db):
self.db = db
def get_user_by_id(self, user_id):
try:
self.db.cursor.execute("SELECT display_name FROM users WHERE id = ?", (user_id,))
user = self.db.cursor.fetchone()
return user
except sqlite3.Error as e:
print(f"An error occurred: {e}")
return None
# main.py
from database import Database
from user_repository import UserRepository
def get_user_by_id(user_id):
with Database('database.db') as db:
user_repo = UserRepository(db)
return user_repo.get_user_by_id(user_id)
# Example usage
if __name__ == "__main__":
user = get_user_by_id(1)
if user:
print(user)
else:
print("User not found or an error occurred.")
```
The revised code improves on the original in several ways:
* The code now implements a basic data access layer.
* The `Database` class handles the connection to the SQLite database, implementing the context manager protocol with the `__enter__` and `__exit__` methods. This ensures that the database connection is properly managed, including committing transactions and closing the connection.
* The `UserRepository` class encapsulates the logic for accessing user data.
* Values for the queries are parameterized to prevent SQL injection.
* Errors are caught, with details printed to the console.
## Further reading
{% data reusables.copilot.example-prompts.further-reading-items %}

View File

@@ -0,0 +1,93 @@
---
title: Refactoring for performance optimization
shortTitle: Performance optimization
intro: '{% data variables.product.prodname_copilot_chat_short %} can suggest ways to speed up slow-running code.'
versions:
feature: copilot
category:
- 'Refactoring code'
complexity:
- Simple
octicon: rocket
topics:
- Copilot
---
Existing code may function correctly but be inefficient, leading to performance bottlenecks. Examples of coding that can adversely impact performance include inefficient loops, unnecessary computations, and excessive memory allocation.
{% data variables.product.prodname_copilot_chat_short %} can identify performance hotspots and suggest refactoring strategies such as optimizing data structures (for example, using hashmaps instead of arrays), reducing redundant calculations, and improving algorithmic efficiency. It can also recommend caching mechanisms or parallelizing operations to improve performance.
## Example scenario
The following Python code prints every 1000th prime number up to the 10,000th prime. It includes a timer, and prints how long the code takes to run as the last line of the output. The program takes several seconds to complete.
```python
import time
def is_prime(n):
if n <= 1:
return False
for i in range(2, n):
if n % i == 0:
return False
return True
def every_1000th_prime():
primes = []
num = 2
while len(primes) < 10000:
if is_prime(num):
primes.append(num)
num += 1
return [primes[i] for i in range(999, 10000, 1000)]
if __name__ == "__main__":
start_time = time.time()
primes = every_1000th_prime()
for prime in primes:
print(prime)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Time taken: {elapsed_time:.10f} seconds")
```
## Example prompt
`Optimize this code for performance`
## Example response
{% data variables.product.prodname_copilot_short %} suggests using a sieve function to generate the prime numbers much more efficiently. If you run this program it will complete in a fraction of a second.
> [!NOTE] {% data variables.product.prodname_copilot_chat_short %} responses are non-deterministic, so you may get a different response from the one shown here.
```python
import time
def sieve_of_eratosthenes(limit):
sieve = [True] * (limit + 1)
sieve[0] = sieve[1] = False
for start in range(2, int(limit**0.5) + 1):
if sieve[start]:
for multiple in range(start*start, limit + 1, start):
sieve[multiple] = False
return [num for num, is_prime in enumerate(sieve) if is_prime]
def every_1000th_prime():
limit = 105000 # Higher than needed to ensure 10,000 primes
primes = sieve_of_eratosthenes(limit)
return [primes[i] for i in range(999, 10000, 1000)]
if __name__ == "__main__":
start_time = time.time()
primes = every_1000th_prime()
for prime in primes:
print(prime)
end_time = time.time()
elapsed_time = end_time - start_time
print(f"Time taken: {elapsed_time:.10f} seconds")
```
## Further reading
{% data reusables.copilot.example-prompts.further-reading-items %}

View File

@@ -0,0 +1,115 @@
---
title: Refactoring to implement a design pattern
shortTitle: Design patterns
intro: '{% data variables.product.prodname_copilot_chat_short %} can suggest design patterns that you can use to improve your code.'
versions:
feature: copilot
category:
- 'Refactoring code'
complexity:
- Intermediate
octicon: rocket
topics:
- Copilot
---
Code that doesnt follow established design patterns (like Singleton, Factory, or Observer) can lead to issues like tight coupling, lack of flexibility, or duplicated code.
{% data variables.product.prodname_copilot_chat_short %} can help you use appropriate design patterns to make your code more flexible and maintainable. For example, it can recommend converting repetitive instantiations into a Factory pattern, or using the Observer pattern to decouple tightly connected classes.
> [!NOTE] The responses described in this article are examples. {% data variables.product.prodname_copilot_chat_short %} responses are non-deterministic, so you may get different responses from the ones shown here.
## Example scenario
This JavaScript code creates three `Person` objects, each with `name` and `age` properties. It then uses the name of a person to retrieve and print their age.
```javascript
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
var people = {};
function addPerson(name, age) {
people[name] = new Person(name, age);
}
function getAge(name) {
if (people[name]) {
console.log(name + " is " + people[name].age + " years old.");
} else {
console.log("Person not found.");
}
}
addPerson("Ana", 30);
addPerson("Mario", 25);
addPerson("Louise", 40);
getAge("Mario");
```
## Example prompt 1
`What design patterns could improve this code? Don't show me code examples.`
## Example response 1
{% data variables.product.prodname_copilot_short %} suggests the following patterns:
* **Singleton pattern**: to ensure that there is only one instance of the `people` object.
* **Factory pattern**: to create a factory for creating `Person` objects.
* **Module pattern**: to encapsulate the `people` object and related functions within a module to avoid polluting the global namespace.
## Example prompt 2
You can now ask {% data variables.product.prodname_copilot_short %} to implement the pattern that you feel is most appropriate.
`Refactor this code using the module pattern`
## Example response 2
```javascript
const PersonModule = (function() {
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const people = {};
function addPerson(name, age) {
people[name] = new Person(name, age);
}
function getAge(name) {
if (people[name]) {
console.log(name + " is " + people[name].age + " years old.");
} else {
console.log("Person not found.");
}
}
return {
addPerson,
getAge
};
})();
PersonModule.addPerson("Ana", 30);
PersonModule.addPerson("Mario", 25);
PersonModule.addPerson("Louise", 40);
PersonModule.getAge("Mario");
```
The module pattern improves code organization, enhances data privacy, and reduces the risk of naming conflicts, making the code more maintainable and scalable.
## Further reading
{% data reusables.copilot.example-prompts.further-reading-items %}