Development Guide
Best practices, workflows, and advanced usage for productive Java 25 development
Development Workflow
Recommended workflow for daily development with the template's tools and configurations.
Start Development
./mvnw compile # Compile and format check
./mvnw test # Run unit tests
Write Code & Tests
Write your implementation in src/main/java
and corresponding tests in src/test/java
Continuous Testing
./mvnw test -Dtest=YourTestClass # Run specific test
./mvnw spotless:apply # Format code
Pre-commit Validation
./mvnw verify # Full build with all tests
Testing Strategy
Comprehensive testing approach with unit tests, integration tests, property-based testing, and architecture validation.
Unit Tests
Fast, isolated tests using JUnit 5 and AssertJ. Filename pattern: *Test.java
./mvnw test # Runs only unit tests
Integration Tests
Component and system integration tests. Filename pattern: *IT.java
./mvnw integration-test # Runs only IT tests
Property-Based Testing
Automated test case generation with jqwik for discovering edge cases.
@Property
void testProperty(@ForAll int value) {
// Test logic with generated values
}
Architecture Tests
Validate architectural rules and code structure with ArchUnit.
@Test
void layersShouldBeRespected() {
layeredArchitecture()
.whereLayer("Controller").mayNotAccessAnyLayer()
.check(classes);
}
Test Configuration Example
Unit Test Example
@ExtendWith(MockitoExtension.class)
class ServiceTest implements WithAssertions {
@Mock
private Repository repository;
@InjectMocks
private Service service;
@Test
void shouldReturnExpectedResult() {
// given
var input = create(InputData.class);
when(repository.find(any())).thenReturn(expectedData);
// when
var result = service.process(input);
// then
assertThat(result).satisfies(data -> {
assertThat(data.getId()).isNotNull();
assertThat(data.getValue()).isEqualTo(expected);
});
}
}
Integration Test Example
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ApplicationIT implements WithAssertions {
private TestContainer container;
@BeforeAll
void startContainer() {
container = new TestContainer().start();
}
@Test
void shouldProcessCompleteWorkflow() {
// Full integration test
var result = application.processWorkflow(testData);
assertThat(result)
.hasFieldOrPropertyWithValue("status", "SUCCESS")
.hasFieldOrPropertyWithValue("processed", true);
}
@AfterAll
void stopContainer() {
container.stop();
}
}
Code Quality & Formatting
Automated code quality enforcement with Spotless and best practices.
๐จ Code Formatting
# Apply formatting to all files
./mvnw spotless:apply
# Check formatting without changes
./mvnw spotless:check
# Format specific files
./mvnw spotless:apply -Dspotless.files=src/main/java/YourClass.java
Uses Google Java Format with AOSP style for consistent code formatting.
โ Quality Gates
Formatting is automatically checked during compilation:
./mvnw compile # Fails if code is not formatted
This ensures all code follows the same style conventions.
IDE Integration
IntelliJ IDEA Setup
- Install "google-java-format" plugin
- Go to Settings โ Other Settings โ google-java-format Settings
- Check "Enable google-java-format"
- Select "AOSP style"
- Enable "Reformat code" and "Optimize imports"
Interactive Development with JShell
Use JShell for rapid prototyping and interactive development.
๐ Starting JShell
# Start JShell with project classpath
./mvnw jshell:run
# Alternative: compile first, then start
./mvnw compile
./mvnw jshell:run
JShell starts with your project's compiled classes and dependencies on the classpath.
๐ก JShell Usage Examples
// Import your classes
import org.acme.*;
// Create instances and experiment
var func = new Function();
var input = new Function.Input(10, 20);
var result = func.add(input);
System.out.println(result);
// Test different scenarios interactively
/exit // Exit JShell
๐ฏ JShell Best Practices
- Use JShell for quick experimentation and API exploration
- Test edge cases before writing formal tests
- Prototype complex logic interactively
- Use
/help
for available JShell commands - Save useful snippets to files with
/save filename.jsh
Maven Wrapper & Commands
Essential Maven commands using the wrapper for consistent builds across environments.
Build Lifecycle
./mvnw clean # Clean build artifacts
./mvnw compile # Compile main sources
./mvnw test-compile # Compile test sources
./mvnw test # Run unit tests
./mvnw package # Create JAR file
./mvnw verify # Run all tests + checks
./mvnw install # Install to local repo
Testing Commands
# Run specific test class
./mvnw test -Dtest=ClassName
# Run specific test method
./mvnw test -Dtest=ClassName#methodName
# Run tests with pattern
./mvnw test -Dtest="*ServiceTest"
# Skip tests during build
./mvnw package -DskipTests
Packaging & Profiles
# Create fat JAR with dependencies
./mvnw package -Pshade
# Clean and package
./mvnw clean package
# Install without running tests
./mvnw install -DskipTests
Analysis & Information
# Display dependency tree
./mvnw dependency:tree
# Check for updates
./mvnw versions:display-dependency-updates
# Display effective POM
./mvnw help:effective-pom
Modularization with JPMS
Java Platform Module System (JPMS) configuration for modern modular applications.
๐ฆ Main Module Configuration
// src/main/java/module-info.java
module org.acme {
exports org.acme;
// Add dependencies as needed
// requires java.logging;
// requires transitive java.base;
}
Configure your main module with exports and required dependencies.
๐งช Test Module Configuration
// src/test/java/module-info.java
open module org.acme.test {
requires org.acme;
requires org.junit.jupiter.api;
requires org.assertj.core;
requires net.jqwik.api;
}
Test module opens packages for reflection-based testing frameworks.
๐ฏ Modularization Best Practices
- Start with a simple module structure and evolve as needed
- Use
exports
to control API surface area - Prefer
requires transitive
for dependencies that are part of your API - Use
open module
in tests to allow reflection access - Consider splitting large applications into multiple modules
CI/CD with GitHub Actions
Automated build, test, and quality checks on every push to the main branch.
๐ Workflow Overview
name: Maven Build
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: "Checkout Sources"
uses: actions/checkout@v5
with:
fetch-depth: 2
- name: "Setup JDK"
uses: actions/setup-java@v5
with:
distribution: "oracle"
java-version: 25
cache: "maven"
- name: "Build with Maven"
run: ./mvnw verify
โ What Gets Validated
- Code Compilation - Java 25 syntax and compilation
- Code Formatting - Spotless formatting checks
- Unit Tests - All *Test.java classes
- Integration Tests - All *IT.java classes
- Package Creation - JAR file generation
๐ Optimization Features
- Maven Caching - Dependencies cached between runs
- Oracle JDK 25 - Latest Java version for CI
- Fast Checkout - Shallow clone with depth=2
- Parallel Execution - Tests run in parallel
- No Transfer Progress - Cleaner CI output
Performance & Optimization
Configuration and tips for optimal performance during development and production.
JVM Configuration
# .mvn/jvm.config
-Xmx2048m -Xms512m -Djava.awt.headless=true
Optimized heap settings and headless mode for better performance.
Maven Configuration
# .mvn/maven.config
-B
--fail-at-end
--no-transfer-progress
Batch mode and optimizations for faster, cleaner builds.
Parallel Testing
# junit-platform.properties
junit.jupiter.execution.parallel.enabled = true
junit.jupiter.execution.parallel.config.strategy = dynamic
Dynamic parallel test execution for faster feedback.
Build Optimization
# Skip tests during development
./mvnw compile -DskipTests
# Offline mode (if all deps cached)
./mvnw compile -o
# Multiple threads
./mvnw compile -T 4
Ready for Production Development?
Start building your Java 25 application with confidence using these proven practices and configurations.