Getting Better Results from GitHub Copilot in IntelliJ IDEA: Lessons from Daily Development
Artificial Intelligence has become a regular part of modern software development. For many developers, GitHub Copilot is now as common as…
Getting Better Results from GitHub Copilot in IntelliJ IDEA: Lessons from Daily Development
Artificial Intelligence has become a regular part of modern software development. For many developers, GitHub Copilot is now as common as IntelliJ IDEA itself. It helps generate code, write tests, explain APIs, refactor methods, and even create documentation.
However, after using GitHub Copilot extensively in IntelliJ IDEA, I noticed something frustrating.
Sometimes it generates poor-quality code. Sometimes it completely misunderstands the requirement. Sometimes it confidently suggests an implementation that is technically correct but architecturally wrong. And occasionally, rewriting the generated code takes longer than writing it from scratch.
If you’ve experienced the same, you’re not alone.
The interesting part is that Copilot usually isn’t the problem.
The biggest factor affecting the quality of the generated code is the context you provide.
This article shares practical lessons from using GitHub Copilot daily in IntelliJ IDEA and explains how to consistently get better code generation, better explanations, and faster development.
Why GitHub Copilot Sometimes Fails
Many developers expect Copilot to behave like a senior engineer who understands the entire project.
In reality, Copilot only knows what it can “see.”
That includes:
- Current file
- Open editor tabs (depending on feature)
- Nearby code
- Comments
- Method names
- Variable names
- Imports
- Project structure (limited)
- Prompt or chat instructions
If important information isn’t available, Copilot starts making assumptions.
Those assumptions often lead to:
- Wrong architecture
- Duplicate logic
- Ignoring existing utilities
- Incorrect business rules
- Unnecessary complexity
The quality of the output is directly proportional to the quality of the context.
Rule #1: Never Start With “Write Code”
Instead of asking: Implement user registration.
Provide context.
Example:
We already have a UserService.
Password hashing uses BCryptPasswordEncoder.
Validation is done using Bean Validation.
Repository methods already exist.
Implement only the service layer.
Do not generate controller or repository.
Throw DuplicateEmailException if email exists.
Return UserDto.
Notice how much architectural information was provided.
The generated solution becomes dramatically better.
Rule #2: Open the Relevant Files
One of the biggest mistakes developers make is asking Copilot to generate code while only one file is open.
Suppose you’re implementing: OrderService
Open these files:
- Order entity
- Customer entity
- OrderRepository
- CustomerRepository
- Existing service
- DTO classes
- Mapper
Now Copilot has far more context.
Instead of inventing methods, it starts reusing existing code patterns.
Rule #3: Keep Your Project Consistent
Copilot learns from your project.
If your project has:
- Some classes use constructor injection
- Some use field injection
- Some use Lombok, Others don't
- Some methods throw exceptions, Others return Optional
Copilot cannot determine which style is preferred.
The result becomes inconsistent.
The cleaner your codebase, the better Copilot performs.
Rule #4: Write Good Method Names
Examples:
- Bad name: process(), Good name: validateAndCreateCustomerOrder()
- Bad name: doWork(), Good name: calculateMonthlySubscriptionPrice()
Copilot heavily relies on method names.
Clear names dramatically improve suggestions.
Rule #5: Use Comments Before Writing Code
Instead of immediately writing code, first write comments.
Example
// Validate request
// Check customer exists
// Verify inventory
// Reserve stock
// Save order
// Publish OrderCreated event
Now ask Copilot to implement.
Most of the generated code follows your intended design.
Rule #6: Build the Skeleton Yourself
Instead of asking for an entire class, first create:
public class PaymentService {
public PaymentResponse processPayment(PaymentRequest request) {}
}
Now ask: Implement processPayment.
Smaller scope means better quality.
Rule #7: Ask for One Responsibility at a Time
Poor prompt: Implement authentication.
Better:
- Generate JWT validation method.
- Generate refresh token logic.
- Generate logout implementation.
- Write unit tests.
Smaller prompts produce significantly higher quality.
Rule #8: Tell Copilot What NOT to Do
Developers often forget this.
Example:
- Implement caching.
- Do not introduce new libraries.
- Do not change repository methods.
- Use existing RedisTemplate.
- Keep implementation thread-safe.
Negative instructions prevent unnecessary creativity.
Rule #9: Use Existing Code as an Example
Instead of saying: Generate another validator.
Say: Implement CustomerValidator similar to ProductValidator.
Copilot becomes much more accurate because it has a reference implementation.
Rule #10: Ask for Refactoring Instead of Generation
Instead of: Rewrite this class.
Try: Refactor this method to reduce nesting. Keep behavior unchanged. Do not change public API. Reduce cyclomatic complexity.
This produces much safer results.
Daily Workflow That Works Well
A productive workflow looks like this:
Step 1
Understand the requirement yourself.
Don’t ask Copilot first.
Step 2
Design the solution.
Think about
- classes
- methods
- responsibilities
- exceptions
- APIs
Step 3
Create empty classes.
Step 4
Write comments describing logic.
Step 5
Generate one method at a time.
Step 6
Review generated code.
Never accept blindly.
Step 7
Run tests.
Step 8
Refactor.
This approach usually produces better code than asking Copilot to generate an entire feature.
Good Prompt vs Bad Prompt
Bad: Implement payment service.
Better: Implement PaymentService.processPayment().
Requirements:
- Validate request.
- Verify customer exists.
- Reject duplicate payment.
- Call existing PaymentGateway.
- Persist transaction.
- Publish PaymentCompletedEvent.
- Return PaymentResponse.
- Use constructor injection.
- Keep transaction atomic.
- Do not modify repository interfaces.
The second prompt provides architectural boundaries.
Asking Copilot to Explain Code
Sometimes explanations are disappointing because the prompt is vague.
Instead of: Explain this.
Ask: Explain this method line by line. Focus on concurrency. Explain why synchronized is required. Mention performance implications. Suggest improvements.
The explanation becomes significantly more useful.
Generating Unit Tests
Instead of: Write tests.
Ask:
Generate JUnit 5 tests.
Use Mockito.
Cover:
- success case
- validation failure
- repository exception
- duplicate record
- null input
- empty collection
Avoid PowerMockito.
Keep one assertion per behavior.This produces much better tests.
This produces much better tests.
Refactoring Legacy Code
Copilot performs well when given explicit constraints.
Example
Refactor this 400-line method.
Constraints:
- Keep behavior identical.
- Extract private methods.
- Reduce duplication.
- Do not change public API.
- Preserve logging.
- Keep exception handling unchanged.
Working with Spring Boot
Instead of: Generate REST API.
Provide:
Spring Boot 3
Java 21
Constructor injection
Bean Validation
Global Exception Handler
MapStruct
JPA
No field injection
No setter injection
Use ResponseEntity
Framework-specific context greatly improves generated code.
Common Mistakes Developers Make
Accepting the First Suggestion
Copilot generates possibilities, not guarantees.
Always review:
- correctness
- security
- performance
- readability
- architecture
Generating Large Files
Generating 500 lines at once usually results in:
- duplicated code
- unused methods
- inconsistent naming
- hidden bugs
Generate incrementally.
Ignoring Existing Project Patterns
If your project already has:
Exception hierarchy
Mapper classes
Utility methods
Validation framework
DTO pattern
Tell Copilot to reuse them.
Otherwise it creates new versions.
Trusting Generated Algorithms
Always verify:
- sorting
- caching
- concurrency
- transactions
- authentication
- authorization
- encryption
These areas deserve manual review.
IntelliJ Features That Improve Copilot Usage
Take advantage of IntelliJ IDEA features alongside Copilot:
- Use Code Inspection to catch issues immediately after accepting generated code.
- Run Reformat Code to keep style consistent.
- Use Optimize Imports to remove unnecessary imports.
- Leverage Rename Refactoring instead of manually renaming generated identifiers.
- Use Find Usages before accepting changes that modify public APIs.
- Run static analysis and existing test suites frequently.
Copilot is most effective when paired with IntelliJ’s built-in developer productivity tools rather than used as a replacement for them.
When You Should Ignore Copilot
There are situations where manual implementation is usually faster and safer:
- Security-sensitive authentication logic
- Authorization rules
- Financial calculations
- Complex SQL optimization
- Distributed transactions
- Performance-critical algorithms
- Low-level concurrency
- Domain-specific business rules
Use Copilot as an assistant, not the final authority.
A Practical Daily Checklist
Before asking Copilot to generate code, ask yourself:
- Have I clearly understood the requirement?
- Have I opened the relevant project files?
- Am I asking for one focused task instead of an entire feature?
- Have I described the constraints and architecture?
- Have I told Copilot what existing utilities or patterns to reuse?
- Have I specified what it should avoid doing?
- Will I review, test, and refactor the generated code before committing?
If the answer is “yes” to most of these questions, the quality of the generated output improves significantly.
Final Thoughts
GitHub Copilot is not a replacement for software engineering judgment — it is a productivity multiplier.
The difference between developers who struggle with Copilot and those who benefit from it often comes down to how they interact with the tool. Clear requirements, focused prompts, project context, and incremental development consistently produce better results than asking Copilot to “build everything.”
Think of Copilot as a capable pair programmer that lacks long-term memory and deep domain knowledge. The more accurately you communicate the problem, constraints, and existing architecture, the more useful its suggestions become.
When used intentionally, GitHub Copilot in IntelliJ IDEA can reduce repetitive work, accelerate implementation, improve documentation, and speed up testing. But the responsibility for code quality, maintainability, and correctness always remains with the developer.
The best developers don’t simply accept AI-generated code — they guide it, review it, refine it, and integrate it into a well-designed software system.
메타데이터
- post_id
- ea4ffe774f60
- slug
- getting-better-results-from-github-copilot-in-intellij-idea-lessons-from-daily-development-ea4ffe774f60
- url
- https://medium.com/@smita.s.kothari/getting-better-results-from-github-copilot-in-intellij-idea-lessons-from-daily-development-ea4ffe774f60
- canonical_url
- https://medium.com/@smita.s.kothari/getting-better-results-from-github-copilot-in-intellij-idea-lessons-from-daily-development-ea4ffe774f60
- author_url
- https://medium.com/@smita.s.kothari
- status
- ok
- fetched_at
- 2026-08-27 20:56:06