1
0
mirror of synced 2025-12-19 09:57:42 -05:00

Add new tutorial on burning down tech debt (#58552)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Kevin Heis <heiskr@users.noreply.github.com>
Co-authored-by: Vanessa <vgrl@github.com>
This commit is contained in:
hubwriter
2025-11-24 09:53:42 +00:00
committed by GitHub
parent f0ce9225b7
commit eee9654592
5 changed files with 438 additions and 25 deletions

View File

@@ -21,6 +21,7 @@ children:
- /explore-pull-requests
- /write-tests
- /refactor-code
- /reduce-technical-debt
- /review-ai-generated-code
- /learn-a-new-language
- /modernize-legacy-code

View File

@@ -0,0 +1,381 @@
---
title: Using GitHub Copilot to reduce technical debt
shortTitle: Reduce technical debt
intro: 'Use {% data variables.product.prodname_copilot_short %} to automate refactoring and maintenance tasks, freeing your team to focus on feature development.'
versions:
feature: copilot
topics:
- Copilot
contentType: tutorials
category:
- Burn down tech debt
- Author and optimize with Copilot
---
## Introduction
Technical debt accumulates in every codebase: duplicate code, missing tests, outdated dependencies, and inconsistent patterns. These issues can accumulate because feature development is typically given a higher priority. This tutorial explains how you can use {% data variables.product.prodname_copilot %} to tackle technical debt systematically, without sacrificing feature velocity.
### Who this tutorial is for
This tutorial is designed to help engineering teams and technical leads reduce technical debt while maintaining the pace at which new features are delivered. You should have:
* A {% data variables.product.prodname_copilot_short %} subscription with access to {% data variables.copilot.copilot_coding_agent %}
* Admin access to at least one repository
* Familiarity with your team's development workflow
### What you'll accomplish
By the end of this tutorial, you'll have learned about:
* Using {% data variables.product.prodname_copilot_short %} to implement in-the-moment fixes
* Leveraging {% data variables.copilot.copilot_coding_agent %} for large-scale cleanup tasks
* Creating custom instructions to align {% data variables.product.prodname_copilot_short %} with your team's standards
* Measuring the impact of {% data variables.product.prodname_copilot_short %} on your technical debt
## Understanding the technical debt problem
Before starting to reduce the technical debt in a codebase, you should take some time to identify the types of technical debt your team faces most often.
Common types of technical debt include:
* **Code duplication** - The same logic implemented in multiple places
* **Missing tests** - Features without adequate test coverage
* **Outdated dependencies** - Libraries several versions behind current releases
* **Inconsistent patterns** - Different approaches to the same problem across your codebase
* **Legacy code** - Old code that works but doesn't follow current standards
The cost of technical debt compounds over time:
* Senior engineers spend time on routine updates instead of architecture design
* Code reviews become longer as reviewers debate inconsistent patterns
* New developers take longer to onboard due to confusing code organization
* Deployment risk increases as outdated dependencies accumulate vulnerabilities
## Using {% data variables.product.prodname_copilot_short %} in your IDE for in-the-moment fixes
The best way to avoid technical debt accumulating in your codebase is to prevent it getting into the codebase in the first place.
When you encounter technical debt during development, fix it immediately using {% data variables.product.prodname_copilot_short %} in your IDE.
### Quick refactoring workflow
1. While working in your IDE, highlight code that needs improvement.
1. Open {% data variables.copilot.copilot_chat_short %} in the IDE.
1. Ask {% data variables.product.prodname_copilot_short %} to refactor the code. For example:
* `Extract this into a reusable helper and add error handling`
* `Standardize this logging format to match our pattern`
* `Add null checks for all optional parameters`
* `Replace this deprecated API call with the current version` <!-- markdownlint-disable-line GHD046 -->
1. Review the suggested changes.
1. Accept the changes or ask {% data variables.product.prodname_copilot_short %} to modify its approach.
1. Run your tests to verify the changes work correctly.
### Example: Standardizing error handling
If you find inconsistent error handling—for example:
```javascript id="err-handling"
// Highlight this code
try {
await fetchData();
} catch (e) {
console.log(e);
}
```
Ask {% data variables.product.prodname_copilot_short %} to improve the code—for example:
```copilot prompt copy ref="err-handling"
Refactor this to use structured logging and proper error handling
```
{% data variables.product.prodname_copilot_short %} might suggest:
```javascript
try {
await fetchData();
} catch (error) {
logger.error('Failed to fetch data', {
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
throw error;
}
```
> [!NOTE] This response is an example. {% data variables.copilot.copilot_chat_short %} responses are non-deterministic, so you may get a different response if you run the same prompt against the same code.
By adopting the in-the-moment fix approach, you help to ensure that substandard code does not get added to your codebase, and you avoid the creation of a backlog issue that may never be addressed.
For more details on using {% data variables.product.prodname_copilot_short %} in your IDE, see [AUTOTITLE](/copilot/using-github-copilot/asking-github-copilot-questions-in-your-ide).
## Using {% data variables.copilot.copilot_coding_agent %} for large-scale refactoring
Some refactoring tasks are just too big to complete while everyone on the team is busy developing new features. In this situation you can use {% data variables.copilot.copilot_coding_agent %} to handle these tasks autonomously. Human effort will still be required—at a minimum for reviewing the changes {% data variables.copilot.copilot_coding_agent %} proposes—but getting {% data variables.product.prodname_copilot_short %} to do the bulk of the work can allow you to carry out large-scale refactoring with much less impact on your team's productivity.
### When to use {% data variables.copilot.copilot_coding_agent %}
Use {% data variables.copilot.copilot_coding_agent %} for tasks that:
* Touch many files across your codebase
* Require systematic changes (like removing old feature flags)
* Need careful testing but are straightforward to implement
* Would interrupt feature development if done manually
Examples include:
* Framework upgrades that affect 50+ files
* Removing deprecated feature flags <!-- markdownlint-disable-line GHD046 -->
* Migrating to strict TypeScript
* Updating dependency versions
* Standardizing import patterns
### Workflow for {% data variables.copilot.copilot_coding_agent %}
1. Create a {% data variables.product.prodname_dotcom %} issue describing the refactoring task.
Be specific about what needs to change. For example:
```markdown
Remove all feature flags marked for cleanup in Q2.
These flags are:
- `enable_new_dashboard`
- `beta_export_feature`
- `experimental_search`
All three flags are enabled by default in production.
Remove the flag checks and keep the "enabled" code path.
```
1. Assign the issue to the **Copilot** user.
1. {% data variables.copilot.copilot_coding_agent %} will:
* Set up a development environment
* Open a draft pull request
* Make the required changes to the code
* Run your tests
* Finalize the pull request for review
* Request your review of the pull request
1. Review the pull request just as you would a pull request raised by a human.
1. Leave comments if changes are needed—{% data variables.copilot.copilot_coding_agent %} will update the pull request based on your feedback.
1. Iterate in this way until the work is completed correctly.
1. Approve and merge the pull request.
For more information, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/coding-agent/create-a-pr#assigning-an-issue-to-copilot) and [AUTOTITLE](/copilot/how-tos/use-copilot-agents/coding-agent/review-copilot-prs).
### Safety guardrails
{% data variables.copilot.copilot_coding_agent %} operates with built-in safety measures:
* It can only push to its own `copilot/*` branches
* It cannot merge pull requests—requires your approval
* All commits are logged and auditable
* Your existing branch protections remain active
* CI/CD checks run before any code is merged
## Creating custom instructions for your team
Custom instructions help {% data variables.product.prodname_copilot_short %} understand your team's coding standards and patterns. This ensures suggestions match your expectations from the start.
### Setting up custom instructions
1. In your repository, create a file named `.github/copilot-instructions.md`.
1. Add your team's coding standards in clear, straightforward statements—for example, using bulleted lists.
1. Commit the file to your repository.
### Example custom instructions
Here's an example of effective custom instructions:
```markdown
## Our Standards
- Use structured logging, not console.log
- Sanitize user input before database queries
- Check for null/undefined on all optional parameters
- Keep functions under 50 lines (extract helpers if needed)
- Every public function needs a test
- Flag any loops that might trigger N+1 queries
## Error Handling
- Always use try-catch blocks for async operations
- Log errors with context (user ID, request ID, timestamp)
- Never swallow errors silently
- Return appropriate HTTP status codes
## Testing Requirements
- Unit tests for all business logic
- Integration tests for API endpoints
- Mock external services in tests
- Test both success and failure paths
```
For detailed guidance on writing custom instructions, see [AUTOTITLE](/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot).
### Benefits of custom instructions
With custom instructions in place:
* {% data variables.product.prodname_copilot_short %} suggests code following your patterns
* Code reviews become faster, with fewer discussions about style changes
* New team members learn your standards through {% data variables.product.prodname_copilot_short %} suggestions
* Consistency improves across your codebase
## Running a pilot program
Start small to validate {% data variables.product.prodname_copilot_short %}'s impact on your technical debt before rolling it out widely.
### Week 1: Set up and establish baselines
1. Ensure all pilot participants have {% data variables.product.prodname_copilot_short %} access with {% data variables.copilot.copilot_coding_agent %} enabled.
1. Count the technical debt items in your backlog:
* Number of "tech debt", "chore", or similar labeled issues
* Number of outdated dependencies
* Number of files failing linter checks
1. Track current metrics:
* Average time from pull request creation to merge for refactoring PRs
* Average number of review rounds per refactoring PR
1. Create your first `.github/copilot-instructions.md` file with 35 of your most important standards.
### Weeks 24: Run the pilot
1. Select 510 repositories for your pilot.
1. Choose 12 specific problems to address. For example:
* Code duplication in a particular area
* Missing tests on frequently changed files
* Outdated dependencies
1. Use {% data variables.product.prodname_copilot_short %} in your IDE for quick fixes as you encounter issues.
1. Assign larger cleanup tasks to {% data variables.copilot.copilot_coding_agent %}.
1. Review all {% data variables.product.prodname_copilot_short %}-generated PRs carefully.
1. Provide feedback on suggestions to help {% data variables.product.prodname_copilot_short %} learn your preferences.
### Week 5: Evaluate results
After the pilot, measure your results:
* How much faster are refactoring pull requests getting merged?
* How many review rounds do they require now?
* Which types of code change suggestions, made by {% data variables.copilot.copilot_coding_agent %} in pull requests, did developers accept most often?
* Which suggestions needed the most revision?
* Are your technical debt metrics improving?
* Linter warnings decreasing?
* Test coverage increasing?
* Dependency versions more current?
Update your custom instructions based on what you learned about which guidance helped {% data variables.product.prodname_copilot_short %} most.
## Measuring success
Track specific metrics to understand {% data variables.product.prodname_copilot_short %}'s impact on your technical debt.
### Velocity metrics
Monitor how {% data variables.product.prodname_copilot_short %} affects development speed:
* Time to close technical debt issues (target: 3050% reduction)
* Number of technical debt pull requests merged per week (target: 23x increase)
* Average number of review cycles per refactoring pull request (assess whether this increased or decreased)
### Quality metrics
Ensure quality improves alongside velocity:
* Linter warning count (this should trend downward)
* Test coverage percentage (this should trend upward)
* Number of production incidents related to refactored code (assess whether this changed)
### Engineer satisfaction
Survey your team regularly:
* Are engineers spending less time on routine maintenance?
* Are code reviews focusing more on architecture and less on style?
* Is onboarding faster for new team members?
## Troubleshooting
### {% data variables.product.prodname_copilot_short %} suggests incorrect changes
If {% data variables.product.prodname_copilot_short %} consistently suggests code that doesn't match your needs:
* Review your custom instructions—they may be too vague or contradictory
* Provide more specific context in your prompts
* Add examples of good code to your custom instructions
* Leave detailed feedback in pull request reviews to allow {% data variables.copilot.copilot_coding_agent %} to fix the problems
### Pull requests are too large to review
If {% data variables.copilot.copilot_coding_agent %} creates pull requests that are difficult to review:
* Break large tasks into smaller, focused issues
* Ask {% data variables.copilot.copilot_coding_agent %} to handle one file or directory at a time
* Use more specific issue descriptions
### Changes break tests
If refactoring introduces test failures:
* Ensure your test suite runs reliably before using {% data variables.copilot.copilot_coding_agent %}
* Review {% data variables.product.prodname_copilot_short %} changes carefully before merging
* Ask {% data variables.product.prodname_copilot_short %} to update tests along with the code changes
### Team adoption is slow
If your team isn't using {% data variables.product.prodname_copilot_short %} for technical debt:
* Share success stories from early adopters
* Demonstrate time savings in team meetings
* Start with the most annoying technical debt items
* Make creating custom instructions a team activity
## Conclusion
In this tutorial, you learned how to use {% data variables.product.prodname_copilot_short %} to systematically reduce technical debt. You now know how to:
* Fix technical debt immediately using {% data variables.product.prodname_copilot_short %} in your IDE
* Assign large refactoring tasks to {% data variables.copilot.copilot_coding_agent %}
* Create custom instructions that align {% data variables.product.prodname_copilot_short %} with your team's standards
* Run a pilot program to validate the approach
* Measure {% data variables.product.prodname_copilot_short %}'s impact on technical debt
By automating routine refactoring and maintenance tasks, {% data variables.product.prodname_copilot_short %} frees you to focus on architecture, feature development, and other high-value work.
### Quick survey
{% note %}
After reading this tutorial, do you feel confident you can use {% data variables.product.prodname_copilot_short %} to reduce the technical debt in a codebase?
<a href="https://docs.github.io/success-test/yes.html" target="_blank" class="btn btn-outline mt-3 mr-3 no-underline"><span>Yes</span></a> <a href="https://docs.github.io/success-test/no.html" target="_blank" class="btn btn-outline mt-3 mr-3 no-underline"><span>No</span></a>
{% endnote %}
## Next steps
* **Expand your pilot**: Roll out to more repositories based on your pilot results.
* **Automate dependency updates**: Create recurring issues for {% data variables.copilot.copilot_coding_agent %} to handle dependency updates.
* **Build a refactoring queue**: Label issues in your backlog as good for {% data variables.product.prodname_copilot_short %} then regularly assign a batch of these to {% data variables.product.prodname_copilot_short %} to work on.
* **Share best practices**: Document successful prompts and custom instructions for your team.
## Further reading
* [AUTOTITLE](/copilot/using-github-copilot/coding-agent)
* [AUTOTITLE](/copilot/tutorials/refactoring-code-with-github-copilot)
* [How to use GitHub Copilot in your IDE: Tips, tricks, and best practices](https://github.blog/developer-skills/github/how-to-use-github-copilot-in-your-ide-tips-tricks-and-best-practices/) in the {% data variables.product.company_short %} blog
* [5 ways to integrate GitHub Copilot coding agent into your workflow](https://github.blog/ai-and-ml/github-copilot/5-ways-to-integrate-github-copilot-coding-agent-into-your-workflow/) in the {% data variables.product.company_short %} blog

View File

@@ -62,7 +62,7 @@
| GHD058 | journey-tracks-liquid | Journey track properties must use valid Liquid syntax | error | frontmatter, journey-tracks, liquid |
| GHD059 | journey-tracks-guide-path-exists | Journey track guide paths must reference existing content files | error | frontmatter, journey-tracks |
| GHD060 | journey-tracks-unique-ids | Journey track IDs must be unique within a page | error | frontmatter, journey-tracks, unique-ids |
| GHD061 | frontmatter-hero-image | Hero image paths must be absolute and point to valid images in /assets/images/banner-images/ | error | frontmatter, images |
| GHD061 | frontmatter-hero-image | Hero image paths must be absolute, extensionless, and point to valid images in /assets/images/banner-images/ | error | frontmatter, images |
| GHD062 | frontmatter-intro-links | introLinks keys must be valid keys defined in data/ui.yml under product_landing | error | frontmatter, single-source |
| [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | deprecated liquid syntax: octicon-<icon-name> | The octicon liquid syntax used is deprecated. Use this format instead `octicon "<octicon-name>" aria-label="<Octicon aria label>"` | error | |
| [search-replace](https://github.com/OnkarRuikar/markdownlint-rule-search-replace) | deprecated liquid syntax: site.data | Catch occurrences of deprecated liquid data syntax. | error | |

View File

@@ -10,7 +10,7 @@ interface Frontmatter {
[key: string]: any
}
// Get the list of valid hero images
// Get the list of valid hero images (without extensions)
function getValidHeroImages(): string[] {
const ROOT = process.env.ROOT || '.'
const heroImageDir = path.join(ROOT, 'assets/images/banner-images')
@@ -21,8 +21,11 @@ function getValidHeroImages(): string[] {
}
const files = fs.readdirSync(heroImageDir)
// Return absolute paths as they would appear in frontmatter
return files.map((file) => `/assets/images/banner-images/${file}`)
// Return absolute paths without extensions as they should appear in frontmatter
return files.map((file) => {
const baseName = path.basename(file, path.extname(file))
return `/assets/images/banner-images/${baseName}`
})
} catch {
return []
}
@@ -31,7 +34,7 @@ function getValidHeroImages(): string[] {
export const frontmatterHeroImage: Rule = {
names: ['GHD061', 'frontmatter-hero-image'],
description:
'Hero image paths must be absolute and point to valid images in /assets/images/banner-images/',
'Hero image paths must be absolute, extensionless, and point to valid images in /assets/images/banner-images/',
tags: ['frontmatter', 'images'],
function: (params: RuleParams, onError: RuleErrorCallback) => {
// Only check index.md files
@@ -70,7 +73,22 @@ export const frontmatterHeroImage: Rule = {
return
}
// Check if the file actually exists
// Check if the path includes a file extension (which is not allowed)
if (path.extname(heroImage)) {
const line = params.lines.find((ln: string) => ln.trim().startsWith('heroImage:'))
const lineNumber = line ? params.lines.indexOf(line) + 1 : 1
const withoutExtension = heroImage.substring(0, heroImage.lastIndexOf('.'))
addError(
onError,
lineNumber,
`Hero image path must not include file extension. Use: ${withoutExtension}`,
line || '',
null, // No fix possible
)
return
}
// Check if a file with this base name actually exists
const validHeroImages = getValidHeroImages()
if (validHeroImages.length > 0 && !validHeroImages.includes(heroImage)) {
const line = params.lines.find((ln: string) => ln.trim().startsWith('heroImage:'))

View File

@@ -6,7 +6,24 @@ import { frontmatterHeroImage } from '../../lib/linting-rules/frontmatter-hero-i
const fmOptions = { markdownlintOptions: { frontMatter: null } }
describe(frontmatterHeroImage.names.join(' - '), () => {
test('valid absolute heroImage path passes', async () => {
test('valid absolute extensionless heroImage path passes', async () => {
const markdown = [
'---',
'title: Test',
"heroImage: '/assets/images/banner-images/hero-1'",
'---',
'',
'# Test',
].join('\n')
const result = await runRule(frontmatterHeroImage, {
strings: { 'content/test/index.md': markdown },
...fmOptions,
})
const errors = result['content/test/index.md']
expect(errors.length).toBe(0)
})
test('heroImage path with extension fails', async () => {
const markdown = [
'---',
'title: Test',
@@ -20,7 +37,8 @@ describe(frontmatterHeroImage.names.join(' - '), () => {
...fmOptions,
})
const errors = result['content/test/index.md']
expect(errors.length).toBe(0)
expect(errors.length).toBe(1)
expect(errors[0].errorDetail).toContain('must not include file extension')
})
test('non-index.md file is ignored', async () => {
@@ -51,14 +69,9 @@ describe(frontmatterHeroImage.names.join(' - '), () => {
})
test('relative heroImage path fails', async () => {
const markdown = [
'---',
'title: Test',
"heroImage: 'images/hero-1.png'",
'---',
'',
'# Test',
].join('\n')
const markdown = ['---', 'title: Test', "heroImage: 'images/hero-1'", '---', '', '# Test'].join(
'\n',
)
const result = await runRule(frontmatterHeroImage, {
strings: { 'content/test/index.md': markdown },
...fmOptions,
@@ -72,7 +85,7 @@ describe(frontmatterHeroImage.names.join(' - '), () => {
const markdown = [
'---',
'title: Test',
"heroImage: '/assets/images/other/hero-1.png'",
"heroImage: '/assets/images/other/hero-1'",
'---',
'',
'# Test',
@@ -90,7 +103,7 @@ describe(frontmatterHeroImage.names.join(' - '), () => {
const markdown = [
'---',
'title: Test',
"heroImage: '/assets/images/banner-images/non-existent.png'",
"heroImage: '/assets/images/banner-images/non-existent'",
'---',
'',
'# Test',
@@ -105,14 +118,14 @@ describe(frontmatterHeroImage.names.join(' - '), () => {
})
test('all valid hero images pass', async () => {
// Test each valid hero image
// Test each valid hero image (extensionless)
const validImages = [
"heroImage: '/assets/images/banner-images/hero-1.png'",
"heroImage: '/assets/images/banner-images/hero-2.png'",
"heroImage: '/assets/images/banner-images/hero-3.png'",
"heroImage: '/assets/images/banner-images/hero-4.png'",
"heroImage: '/assets/images/banner-images/hero-5.png'",
"heroImage: '/assets/images/banner-images/hero-6.png'",
"heroImage: '/assets/images/banner-images/hero-1'",
"heroImage: '/assets/images/banner-images/hero-2'",
"heroImage: '/assets/images/banner-images/hero-3'",
"heroImage: '/assets/images/banner-images/hero-4'",
"heroImage: '/assets/images/banner-images/hero-5'",
"heroImage: '/assets/images/banner-images/hero-6'",
]
for (const heroImageLine of validImages) {