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 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 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 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 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 - 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 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 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.
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 accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more
How to do this with fenbs
- Create a free account and add a project for the Java codebase.
- 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.
- Add your Copilot instructions to the repository in
.github/copilot-instructions.md. Keep the instructions file consistent with the Decisions record. - Connect GitHub Copilot to fenbs over MCP from Integrations. Copilot can read current project rules at the start of a session.
- When a rule changes, update both the instructions file and the Decision record. Record the superseded decision so the history is available.