A ready-to-use Copilot configuration for Java teams — covering legacy codebases (JSP/Struts, Java 7-style) and modern Spring Boot (Java 17+). Copy .github/ into your repo and Copilot becomes aware of your team's quality standards, architectural rules, and migration strategy.
cp -r .github/ /path/to/your-java-repo/.github/Commit the .github/ directory. Copilot will pick up the files automatically in VS Code.
For agents, ensure your team has GitHub Copilot Chat enabled in VS Code (extension: GitHub.copilot-chat).
| Path | Purpose |
|---|---|
.github/copilot-instructions.md |
Repo-wide quality standard — always active |
.github/skills/java-error-handling/ |
Sealed errors, Optional, no exception swallowing |
.github/skills/java-streams/ |
Stream pipelines over imperative loops |
.github/skills/java-testing/ |
JUnit 5 + Mockito + AssertJ patterns |
.github/skills/java-domain-modeling/ |
Records, sealed interfaces, value objects |
.github/skills/spring-architecture/ |
Layering, DI, transactions, DTOs |
.github/skills/legacy-modernization/ |
Strangler fig, anti-corruption, safe migration |
.github/agents/spring-expert.agent.md |
Architecture guidance for Spring Boot |
.github/agents/legacy-modernizer.agent.md |
Safe JSP/Struts → Spring Boot migration |
.github/agents/test-writer.agent.md |
JUnit 5, Mockito, Testcontainers |
.github/skills/java-observability/ |
SLF4J + MDC, structured logging, Micrometer, Actuator |
.github/skills/java-data-access/ |
JPA N+1, fetch strategies, pagination, projections |
.github/skills/api-design/ |
REST conventions, error envelope, versioning, OpenAPI |
.github/hooks/pre-command.sh + pre-command.json |
Block dangerous shell and SQL operations |
.github/hooks/post-edit.sh + post-edit.json |
Code smell suggestions after Java edits |
.githooks/pre-commit |
Block commits with vulnerable Maven/Gradle dependencies |
.githooks/commit-msg |
Enforce Conventional Commits format |
.githooks/pre-push |
Run tests before any push |
scripts/setup-hooks.sh |
Install .githooks/ into .git/hooks/ |
.gitlab-ci.yml |
Build, test, and dependency scan on push/MR |
.gitlab/merge_request_templates/Default.md |
MR checklist: what/why/testing/architecture |
Injected into every Copilot suggestion, regardless of what file is open or what task is underway. Keep this file to non-negotiable rules: the quality standard, forbidden patterns, and layering laws. The shorter it is, the more it actually influences behaviour.
Skills are loaded by Copilot when they're relevant to the current task — not on every file open. Each skill has a SKILL.md with a description field that tells Copilot when to use it. The description is the routing key: it should say both what the skill covers and the conditions under which it applies.
.github/skills/
└── java-error-handling/
└── SKILL.md ← frontmatter: name + description (routing key)
← body: the actual patterns and examples
This matters: a skill about stream pipelines costs zero context when you're writing a domain model. With always-on instruction files, you pay for everything all the time.
Write the description carefully. "Use when writing or reviewing error handling, exception types, or null safety in Java code" loads the skill at the right moment. "Java patterns" loads it for everything.
Show, don't describe. Every principle should have a before/after code block. Copilot reasons in code. Abstract principles without examples are ignored.
Agents are purpose-built assistants you invoke with @agent-name in Copilot Chat.
Use when designing or reviewing Spring Boot code: service layer, JPA, REST controllers, transaction boundaries, DI wiring. Ask it to review a class for architectural violations or to generate new code that follows your team's conventions.
@spring-expert review this service class for layering violations
@spring-expert generate a REST endpoint for creating an order
Use when touching legacy JSP/Struts code or planning a migration. It will assess risk, identify seams, and propose incremental steps rather than rewrites.
@legacy-modernizer how do I extract this Struts Action into a Spring service?
@legacy-modernizer what's the safest way to add a new endpoint alongside this legacy flow?
Use when you need tests for existing code, or want to validate test quality. It writes behavior-focused tests, not implementation tests — your tests survive refactors.
@test-writer write unit tests for OrderService.createOrder
@test-writer add an integration test for the /orders POST endpoint using Testcontainers
Each hook consists of two files: a .sh script that contains the logic, and a .json config that registers it with Copilot.
Runs before Copilot executes a terminal command. Blocks destructive operations:
- Force push without
--force-with-lease rm -rfon root pathsDROP TABLE/DROP DATABASE/DROP SCHEMADELETE FROMwithout aWHEREclauseTRUNCATE TABLEchmod 777
Adapt the patterns in pre-command.sh to match your team's guardrails.
Enforces Conventional Commits format on every commit message.
<type>(<optional scope>): <description>
Allowed types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert. Append ! for breaking changes. The hook prints examples and the full type list when the format is wrong.
To bypass: SKIP_COMMIT_MSG=1 git commit ...
Runs the full test suite (mvn test / ./gradlew test) before any push. Detects Maven or Gradle automatically. Blocks the push if tests fail and prints the bypass command.
To bypass: SKIP_TESTS=1 git push ...
Scans pom.xml (Maven) or build.gradle / build.gradle.kts (Gradle) for known CVEs using the OWASP Dependency-Check plugin before every commit that stages a build file. Blocks the commit if any dependency has a CVSS score ≥ 7 (configurable).
Setup — run once after cloning:
./scripts/setup-hooks.shBehaviour:
- Only triggers when
pom.xml,build.gradle, orbuild.gradle.ktsis staged — other commits are unaffected - Writes a full HTML report to
target/dependency-check-report/(Maven) orbuild/dependency-check-report/(Gradle) - On first run, downloads the NVD database (~few minutes); subsequent runs are fast
- If both build files are staged (polyglot repo), both are scanned
Gradle setup — the OWASP plugin must be applied in your build file before the hook can scan it. The hook will print setup instructions if it's missing:
// build.gradle
plugins {
id "org.owasp.dependencycheck" version "10.0.4"
}
dependencyCheck {
failBuildOnCVSS = 7
outputDirectory = "build/dependency-check-report"
suppressionFile = "dependency-check-suppressions.xml" // optional
}// build.gradle.kts
plugins {
id("org.owasp.dependencycheck") version "10.0.4"
}
dependencyCheck {
failBuildOnCVSS = 7.0f
outputDirectory = "build/dependency-check-report"
suppressionFile = "dependency-check-suppressions.xml" // optional
}Configuration:
| Variable | Default | Purpose |
|---|---|---|
CVSS_THRESHOLD |
7 |
Minimum score to fail (0–10) |
SKIP_DEP_CHECK |
0 |
Set to 1 to bypass for a single commit |
# Raise threshold to critical only
CVSS_THRESHOLD=9 git commit -m "..."
# Emergency bypass
SKIP_DEP_CHECK=1 git commit -m "..."Suppressing false positives — if a CVE does not apply to your usage, add a suppression file:
# Create dependency-check-suppressions.xml in the repo root
# See: https://jeremylong.github.io/DependencyCheck/general/suppression.htmlRuns after Copilot edits a .java file. Surfaces common code smells:
forloops over collections → suggest stream pipelinescatch (Exception/catch (Throwable→ catch specific typesOptional.get()without guard → unsafe access risknew RuntimeException(/new Exception(→ define a typed domain exception@Autowiredon a field → constructor injectionreturn null→ consider returningOptional<T>System.out.println→ use SLF4J logger
These are suggestions, not blocks. The developer sees the message and decides.
.gitlab-ci.yml runs on every push and merge request. It auto-detects Maven or Gradle using rules: exists: and runs only the relevant jobs.
| Job | Stage | What it does |
|---|---|---|
build-maven / build-gradle |
build |
Compile + run full test suite |
dep-check-maven / dep-check-gradle |
security |
OWASP Dependency-Check scan (non-blocking by default) |
Making the dependency scan blocking: remove allow_failure: true from the relevant dep-check job in .gitlab-ci.yml. The local pre-commit hook enforces it before a commit lands; the CI job closes the gap for contributors who bypassed the hook.
Failed test reports and CVE scan reports are uploaded as job artifacts (retained 7 and 30 days respectively).
.gitlab/merge_request_templates/Default.md auto-populates the description when an MR is opened in GitLab. It includes:
- What changed — 2-4 bullets describing the change
- Why — motivation and ticket link
- Testing done — checklist: unit tests, integration tests, manual verification
- Architecture checklist — controller/service separation, constructor injection, transaction placement, ControllerAdvice, no entity leakage
When a bug is hard to pin down, open four Copilot Chat sessions in parallel — one per hypothesis — rather than exploring sequentially. Each session gets a single focused question. Compare conclusions before acting.
Example for a NullPointerException in order processing:
| Session | Hypothesis |
|---|---|
| 1 | order.getCustomer() is null — trace where customer is set |
| 2 | @Transactional boundary issue — lazy-loaded relation accessed outside session |
| 3 | Race condition — two threads modify the same entity |
| 4 | Missing null guard in a recent commit — git log --oneline -20 -- OrderService.java |
All four run at the same time. You converge on the answer faster than one long conversation that goes down wrong paths.
Before asking Copilot to fix code in a review:
- Name the problem first. Ask
@spring-expert what's wrong with this code?before asking it to fix anything. You need to understand the issue, not just apply the patch. - Verify the fix composes. If the suggested fix adds a null check, ask why the value is null. Compensatory fixes (patching the symptom) accumulate into unmaintainable code.
- Check the boundary. Every service method should be reviewable for: what comes in (input type), what goes out (return type or exception), and what it depends on (injected services). If any of these are unclear, that's the review feedback.
Adding a skill:
- Create
.github/skills/<topic>/SKILL.md - Set
nameto match the directory name - Write a
descriptionthat says what it covers and when to use it — this is how Copilot decides to load it - Add before/after examples — minimum two per principle
- Add a row to the table above
Adding an agent:
- Create
.github/agents/<name>.agent.md - Keep the system prompt under 500 words — specificity beats length
- Document usage in the Agents section above
Adding a git hook:
- Drop an executable script into
.githooks/with the standard git hook name (e.g.pre-push,commit-msg) - Re-run
./scripts/setup-hooks.shto install it - Document it in the Hook System section above
Raising the quality bar:
Edit .github/copilot-instructions.md. This file applies to every suggestion in the repo. Be deliberate — every line costs context budget on every interaction.