GitHub Copilot Custom Instructions for Java Projects

Custom instructions let Copilot follow your Java project's build tools, testing conventions, package structure and style rules without you repeating them every session. Here are the examples to start from.

Updated 4 min read

GitHub Copilot's custom instructions let you tell the assistant about your project's conventions once, so it applies them consistently without you repeating them in each chat or inline suggestion. For Java projects, the most useful instructions cover your build tool, testing framework, package boundaries and naming conventions. Tools like fenbs let you store the decisions that explain why those rules exist, so the next developer or session understands the reasoning, not just the instruction.

You can set instructions in two ways: through a .github/copilot-instructions.md file checked into your repository, or through Copilot's settings in your editor. Check the GitHub Copilot documentation for the current supported paths in your editor version, since the exact location has changed across releases. The repository file is usually the better starting point because the whole team shares it without individual setup.

Build tool instructions

Copilot does not always know which build tool you are using. Without guidance, it may suggest Maven POM changes when you use Gradle, or the reverse. State the tool and version upfront.

Build tool example (Maven)
## Build tool
This project uses Maven 3.9 with Java 21. Use `mvn` commands, not Gradle. Dependency declarations belong in `pom.xml`. Do not suggest the Gradle wrapper or Gradle DSL syntax.
Build tool example (Gradle)
## Build tool
This project uses Gradle 8 with the Kotlin DSL (`build.gradle.kts`). Use `./gradlew` commands. Do not suggest Maven POM syntax or `pom.xml` changes.

Testing framework instructions

Java projects vary in their testing setup: JUnit 4 or JUnit 5, Mockito or EasyMock, AssertJ or Hamcrest. An instruction that names the frameworks in use prevents Copilot from mixing them.

Testing framework example
## Testing
Tests use JUnit 5 (`@Test` from `org.junit.jupiter.api`) and Mockito for mocks. Use AssertJ for assertions (`assertThat(...)`). Do not use JUnit 4 annotations, EasyMock, Hamcrest matchers, or raw JUnit `assertEquals`.

Test class names end in `Test`. Integration tests end in `IT` and live in the `src/test/java/.../integration/` package.

One test class per production class. Mock external dependencies; do not call real services in unit tests.

Package boundary instructions

Package boundaries in a layered Java application are often rules people decided early and now take for granted. Copilot does not know them unless you say so.

Package boundaries example
## Package structure
The application follows a layered architecture:
- `com.example.app.controller` — REST endpoints only, no business logic
- `com.example.app.service` — business logic, no JPA entities as parameters or return values
- `com.example.app.repository` — Spring Data JPA interfaces, no business logic
- `com.example.app.domain` — JPA entities and value objects
- `com.example.app.dto` — DTOs for API request and response bodies

Controllers call services. Services call repositories. No layer calls back up. Entities do not leave the service layer; controllers receive and return DTOs.

Naming and style instructions

Naming conventions are often enforced by code review because they are hard to express in a linter. Stating them explicitly in your Copilot instructions means the assistant applies them in suggestions rather than generating something that will be rejected.

Naming conventions example
## Naming conventions
- Classes: PascalCase, nouns (`OrderService`, not `HandleOrders`).
- Methods: camelCase, verbs (`findById`, `calculateTotal`, not `id`, `total`).
- Boolean methods: `is`, `has` or `can` prefix (`isActive`, `hasPermission`).
- Constants: `UPPER_SNAKE_CASE`.
- Test methods: describe intent, not implementation (`shouldReturnEmptyListWhenNoResultsFound`).
- Avoid abbreviations except well-known ones (`id`, `dto`, `url`).

Exception and error handling instructions

Error handling example
## Error handling
Use checked exceptions for expected error cases the caller should handle. Use unchecked exceptions (`RuntimeException` subclasses) for programming errors.

Do not swallow exceptions with empty catch blocks. Log the exception before re-throwing or handle it explicitly. Avoid `printStackTrace()`. Use the project's logging framework (`slf4j` with `LoggerFactory`).

Custom exceptions live in `com.example.app.exception` and include a descriptive message in the constructor.

Logging instructions

Copilot frequently generates print statements or stack traces when producing example code. If your project uses a specific logging framework, an instruction prevents that pattern from appearing in generated code.

Logging example
## Logging
Use SLF4J with LoggerFactory for all logging. Declare the logger as:
  private static final Logger log = LoggerFactory.getLogger(ClassName.class);

Do not use System.out.println, System.err.println, java.util.logging, or Log4j directly. Do not call printStackTrace(). Log at the appropriate level: DEBUG for diagnostic detail, INFO for significant state changes, WARN for recoverable issues, ERROR for failures that need attention.

Combining instructions across a multi-module project

A large Java project may have modules with different conventions. A batch processing module might have different threading rules than a REST API module. You can place a root instructions file with the rules that apply everywhere, and add a module-level file for rules specific to that part of the codebase.

Keep the root file short and focused on the conventions that no module overrides. Module-level files should state their additions clearly rather than repeating the root rules. When the two disagree, Copilot will try to reconcile them; explicit wording reduces ambiguity.

Review the instructions files periodically alongside your code review guidelines. If you are catching the same issue in review that an instruction should prevent, the instruction is either missing, poorly worded or not being applied consistently. Revise it and note the change in your project record so the team knows.

What instructions cannot do

Custom instructions are guidance, not enforcement. Copilot may still produce suggestions that deviate, especially in ambiguous situations or when a suggestion spans several files. Instructions work best for the conventions your team repeats often and discusses in code review.

For strict enforcement, use tools with hard rules: Checkstyle or PMD for naming and style, SpotBugs for bug patterns, and ArchUnit for architectural constraints like your layering rules. Instructions and static analysis are complementary. Instructions shape the suggestion before it is written; static analysis catches what slips through.

A practical starting point is to run a week's worth of code reviews and note the feedback that came up more than once. Each recurring comment is a candidate for an instruction. Phrase it as a clear statement rather than a question, and include a brief rationale where the reason is not obvious. "Do not use Optional as a method parameter" is more useful than "consider alternatives to Optional parameters".

Storing the decisions behind the rules

Instructions tell Copilot what to do. They do not say why. When a new developer joins the project and asks why services never return entities, the instruction file has no answer. When the team debates changing the rule, there is no record of the original reasoning.

Keeping the reasoning with the rule in a shared record prevents the same discussion from happening again, and helps a future assistant give advice that fits the project's actual intent. For more on writing effective Copilot instructions, see Copilot instructions examples.

How fenbs helps

Keep the decisions that explain the rules

Record each architectural rule as a Decision in fenbs: who decided it, when, and why. When Copilot's instructions say "controllers receive and return DTOs", the Decision record explains that this was chosen to prevent lazy-loading bugs in serialization. Connect Copilot to fenbs over MCP so it can read current project rules alongside the instructions file.

Create a free account

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

How to do this with fenbs

  1. Create a free account and add a project for the Java codebase.
  2. Write the technical decisions as Decisions and rules: the build tool choice, the test framework choice, the layering rule. Include the person who decided each one and the reason.
  3. Add your Copilot instructions to the repository in .github/copilot-instructions.md. Keep the instructions file consistent with the Decisions record.
  4. Connect GitHub Copilot to fenbs over MCP from Integrations. Copilot can read current project rules at the start of a session.
  5. When a rule changes, update both the instructions file and the Decision record. Record the superseded decision so the history is available.

Questions people ask.

Where do I put GitHub Copilot custom instructions for a Java project?

The most portable location is the copilot-instructions.md file inside the .github folder at your repository root. This file is checked in and shared with the whole team. Some editors also support per-user or per-workspace settings; check the GitHub Copilot documentation for the current options in your editor version.

How specific should the instructions be?

Specific enough to prevent the errors that come up most often in your code reviews or pull request feedback. Start with the things you find yourself correcting repeatedly: the testing framework, the layering rule, the naming conventions. Add more as you notice gaps.

Will Copilot always follow the instructions?

Copilot treats instructions as strong guidance, not as hard constraints. It may occasionally produce suggestions that deviate, particularly for complex or ambiguous requests. Review generated code as you would any other suggestion.

Can I have different instructions for different modules?

You can add a copilot-instructions.md file closer to the relevant code, such as in a module subdirectory, alongside the root one. Check the GitHub Copilot documentation for how multiple instruction files are combined in your editor version.

How do custom instructions differ from `.editorconfig` or Checkstyle rules?

Editor config and static analysis tools enforce rules mechanically at the character or syntax level. Custom instructions are natural-language guidance for the AI assistant. Both are useful: use static analysis to catch what can be checked automatically, and custom instructions for the conventions that require judgment.

Start with one thing.

Free for one person · No credit card · Works with Claude, ChatGPT, Cursor and more

There is nothing to set up first. Write one line and you’ve started.