Stop the slop.Ship to prod.
Curated Java best practices, packaged as an easy-to-install skill, compatible with any coding agent.
$npx skills add https://github.com/virtuslab/agent-skills --skill java-best-practices
From outdated to outstanding
Stop settling for AI's first draft
Agent's output
WITHOUT SKILLS
public class TeamMatcher {public List<List<String>> find(List<Engineer> engineers) {List<List<String>> teams = new ArrayList<>();List<String> currentTeam = new ArrayList<>();for (int i = 0; i < engineers.size(); i++) {Engineer e = engineers.get(i);if (e.getLanguages().contains("Java")) {currentTeam.add(e.getNickname());if (currentTeam.size() == 5) {teams.add(currentTeam);currentTeam = new ArrayList<>();}}}if (!currentTeam.isEmpty()) teams.add(currentTeam);return teams;}}
WITH SKILLS
enum Language { JAVA, KOTLIN, SCALA }record Engineer(String nickname, Set<Language> languages) {boolean knows(Language language) {return languages.contains(language);}}record Team(Set<Engineer> members) {}private static final int EFFECTIVE_TEAM_SIZE = 5;List<Team> potentiallyGoodTeams(List<Engineer> engineers) {return engineers.stream().filter(e -> e.knows(Language.JAVA)).gather(Gatherers.windowFixed(EFFECTIVE_TEAM_SIZE)).map(Set::copyOf).map(Team::new).toList();}
Records for data objects
Read more inSKILL.md@Valuepublic class User {String username;String email;}
public record User(String username, String email) {}
Sealed classes for domain modeling
Read more inSKILL.mdpublic interface PaymentResult {}public class PaymentSuccess implements PaymentResult { ... }public class PaymentFailure implements PaymentResult { ... }// No compile-time exhaustiveness checking
public sealed interface PaymentResultpermits PaymentSuccess, PaymentFailure, PaymentPending {}public record PaymentSuccess(String txId, Money amount)implements PaymentResult {}public record PaymentFailure(String reason, ErrorCode code)implements PaymentResult {}public record PaymentPending(String txId)implements PaymentResult {}
Switch expressions with pattern matching
Read more inSKILL.mdString message;switch (result) {case PaymentSuccess s:message = "Paid " + s.amount();break;case PaymentFailure f:message = "Failed: " + f.reason();break;case PaymentPending p:message = "Pending: " + p.transactionId();break;}
String message = switch (result) {case PaymentSuccess success ->"Paid %s".formatted(success.amount());case PaymentFailure failure ->"Failed: %s".formatted(failure.reason());case PaymentPending pending ->"Pending: %s".formatted(pending.transactionId());};
Pattern matching for instanceof
Read more in SKILL.mdif (obj instanceof String) {String s = (String) obj;if (!s.isBlank()) {return s.trim();}}
if (obj instanceof String s && !s.isBlank()) {return s.trim();}
Record patterns (destructuring)
Read more inSKILL.mdif (obj instanceof Point p) {System.out.println(p.x() + ", " + p.y());}String desc = switch (shape) {case Circle circle ->"Circle r=" + circle.radius();case Rect rect ->"Rect " + rect.width() + "x" + rect.height();};
if (obj instanceof Point(int x, int y)) {System.out.println(x + ", " + y);}String desc = switch (shape) {case Circle(double radius) ->"Circle r=%s".formatted(radius);case Rect(double width, double height) ->"Rect %sx%s".formatted(width, height);};
Unnamed variables
Read more inSKILL.mdcatch (Exception ignored) { ... }list.stream().map(x -> "constant").toList();
catch (Exception _) { ... }list.stream().map(_ -> "constant").toList();
Text blocks for multi-line strings
Read more inSKILL.mdString query = "SELECT u.id, u.name " +"FROM users u " +"WHERE u.active = true " +"ORDER BY u.name";
String query = """SELECT u.id, u.nameFROM users uWHERE u.active = trueORDER BY u.name""";
var when type is obvious
Read more in SKILL.mdList<User> users = userRepository.findAll();ObjectMapper mapper = new ObjectMapper();
var users = userRepository.findAll();var mapper = new ObjectMapper();
Null safety with Optional
Read more in SKILL.mdpublic User findByEmail(String email) {return repository.get(email); // might return null}
public Optional<User> findByEmail(String email) {return Optional.ofNullable(repository.get(email));}
Error handling
Read more inSKILL.mdpublic User getUser(UserId id) {try {return userRepository.findById(id).orElse(null);} catch (Exception e) {return null;}}
public User getUser(UserId id) {return userRepository.findById(id).orElseThrow(() -> new UserNotFoundException(id));}
Immutable collections
Read more inSKILL.mdvar roles = Arrays.asList("ADMIN", "USER");var config = new HashMap<>(Map.of("timeout", 30));var tags = Collections.unmodifiableList(Arrays.asList("java", "backend"));
var roles = List.of("ADMIN", "USER");var config = Map.of("timeout", 30, "retries", 3);var tags = Set.of("java", "backend");
Sequenced collections
Read more inSKILL.mdvar first = list.get(0);var last = list.get(list.size() - 1);var rev = new ArrayList<>(list);Collections.reverse(rev);
var first = list.getFirst();var last = list.getLast();var rev = list.reversed();
String utilities
Read more inSKILL.mdstr.trim().isEmpty()str.trim()String.format("Hello %s", name)for (String line : text.split("\n")) { ... }
str.isBlank()str.strip() // Unicode-aware"Hello %s".formatted(name)"-".repeat(80) // 80 dashestext.lines() // Stream<String>
Streams for collection transformations
Read more inSKILL.mdvar activeEmails = new ArrayList<String>();for (User user : users) {if (user.isActive()) {activeEmails.add(user.getEmail());}}
var activeEmails = users.stream().filter(User::isActive).map(User::email).toList();
Virtual threads over reactive chains
Read more inSKILL.mdMono.fromCallable(() -> fetchData(url)).subscribeOn(Schedulers.boundedElastic()).flatMap(data -> process(data)).subscribe();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {var future = executor.submit(() -> fetchData(url));return future.get();}
Structured concurrency
Read more inSKILL.mdvar executor = Executors.newFixedThreadPool(2);var f1 = executor.submit(() -> fetchUser(id));var f2 = executor.submit(() -> fetchOrder(id));executor.shutdown();return new Response(f1.get(), f2.get());
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {var user = scope.fork(() -> fetchUser(id));var order = scope.fork(() -> fetchOrder(id));scope.join().throwIfFailed();return new Response(user.get(), order.get());}
Parameterized logging
Read more inSKILL.mdlog.info("Processing order " + orderId + " for user " + userId);
log.info("Processing order {} for user {}", orderId, userId);
Composed method pattern
Read more inSKILL.mdpublic void processOrder(Order order) {if (order.getItems().stream().mapToDouble(Item::getPrice).sum() > 100) {order.setDiscount(0.1);}emailService.send(order.getCustomer().getEmail(),"Order " + order.getId() + " confirmed");repository.save(order);}
public void processOrder(Order order) {applyDiscountIfEligible(order);notifyCustomer(order);repository.save(order);}
Know your stdlib
Read more inSKILL.mdMath.max(min, Math.min(max, value))str.substring(5, 20).indexOf("@")Set<Integer> emojis = Set.of(0x1F600, ...);emojis.contains(codePoint)
Math.clamp(value, min, max)str.indexOf("@", 5, 20) // no allocationCharacter.isEmoji(codePoint)
Keep stream pipelines pure
Read more inSKILL.md// Side effect in intermediate operationusers.stream().map(user -> {emailService.send(user);return user.email();}).toList();// Unnecessary stream() when there is no pipelineusers.stream().forEach(user -> emailService.send(user));
// Iterable.forEach for simple side effectsusers.forEach(user -> emailService.send(user));// Pure filter, side effect only in terminal forEachusers.stream().filter(User::isActive).forEach(user -> emailService.send(user));// Plain loop when you need break/continuefor (var user : users) {emailService.send(user);}
Modern file I/O
Read more inSKILL.mdvar reader = new BufferedReader(new FileReader("config.json"));var sb = new StringBuilder();String line;while ((line = reader.readLine()) != null) {sb.append(line);}reader.close();
String content = Files.readString(Path.of("config.json"));Files.writeString(Path.of("output.txt"), data);
Built-in HttpClient
Read more in SKILL.mdvar conn = (HttpURLConnection) new URL(url).openConnection();conn.setRequestMethod("GET");var in = conn.getInputStream();// ... manual stream reading
var client = HttpClient.newHttpClient();var request = HttpRequest.newBuilder().uri(uri).GET().build();var response = client.send(request, BodyHandlers.ofString());
java.time over legacy Date
Read more in SKILL.mdDate now = new Date();Calendar cal = Calendar.getInstance();cal.add(Calendar.DAY_OF_MONTH, 7);Date nextWeek = cal.getTime();
var now = Instant.now();var nextWeek = LocalDate.now().plusDays(7);var meeting = LocalDate.of(2024, Month.MARCH, 15).atTime(10, 0).atZone(ZoneId.of("UTC"));
Use Spotless after each agent's change
Read more in SKILL.mdpublic List<String>getActiveUsers(){return users.stream().filter(User::isActive).map(User::getName).collect(Collectors.toList());}
public List<String> getActiveUsers() {return users.stream().filter(User::isActive).map(User::getName).toList();}
and many more practices on GitHub
$npx skills add https://github.com/virtuslab/agent-skills --skill java-best-practices







