api-contract-review
Review REST API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when user asksโฆ
Detect potential code-level performance smells in Java - streams, collections, boxing, regex, object creation. Provides awareness, not absolutes - always measure before optimizing. For JPA/database performance, use jpa-patterns instead.
Claude Code
$ npx -y skills add decebals/claude-code-java --skill performance-smell-detection --agent claude-code
Codex
$ npx skills add decebals/claude-code-java --skill performance-smell-detection -a codex -y
Cursor
$ npx skills add decebals/claude-code-java --skill performance-smell-detection -a cursor -y
Gemini CLI
$ npx skills add decebals/claude-code-java --skill performance-smell-detection -a gemini-cli -y
GitHub Copilot
$ npx skills add decebals/claude-code-java --skill performance-smell-detection -a github-copilot -y
OpenCode
$ npx skills add decebals/claude-code-java --skill performance-smell-detection -a opencode -y
Windsurf
$ npx skills add decebals/claude-code-java --skill performance-smell-detection -a windsurf -y
name: performance-smell-detection description: Detect potential code-level performance smells in Java - streams, collections, boxing, regex, object creation. Provides awareness, not absolutes - always measure before optimizing. For JPA/database performance, use jpa-patterns instead. license: MIT
Identify **potential** code-level performance issues in Java code.
> "Premature optimization is the root of all evil" - Donald Knuth
This skill helps you **notice** potential performance smells, not blindly "fix" them. Modern JVMs (Java 21/25) are highly optimized. Always:
1. **Measure first** - Use JMH, profilers, or production metrics 2. **Focus on hot paths** - 90% of time spent in 10% of code 3. **Consider readability** - Clear code often matters more than micro-optimizations
**This skill:** Code-level performance (streams, collections, objects) **For database:** Use `jpa-patterns` skill (N+1, lazy loading, pagination) **For architecture:** Use `architecture-review` skill
---
| Smell | Severity | Context | |-------|----------|---------| | Regex compile in loop | ๐ด High | Always worth fixing | | String concat in loop | ๐ก Medium | Still valid in Java 21/25 | | Stream in tight loop | ๐ก Medium | Depends on collection size | | Boxing in hot path | ๐ก Medium | Measure first | | Unbounded collection | ๐ด High | Memory risk | | Missing collection capacity | ๐ข Low | Minor, measure if critical |
---
Since **Java 9** (JEP 280), string concatenation with `+` uses `invokedynamic`, not StringBuilder. The JVM optimizes simple concatenation well.
**Java 25** adds String::hashCode constant folding for additional optimization in Map lookups with String keys.
// ๐ด Still problematic - new String each iteration
String result = "";
for (String s : items) {
result += s; // O(nยฒ) - creates n strings
}
// โ
StringBuilder for loops
StringBuilder sb = new StringBuilder();
for (String s : items) {
sb.append(s);
}
String result = sb.toString();
// โ
Or use String.join / Collectors.joining
String result = String.join("", items);// โ Fine in Java 9+ - JVM optimizes this String message = "User " + name + " logged in at " + timestamp; // โ Also fine return "Error: " + code + " - " + description;
// ๐ก String.format has parsing overhead
log.debug(String.format("Processing %s with id %d", name, id));
// โ
Parameterized logging (SLF4J)
log.debug("Processing {} with id {}", name, id);---
Streams have overhead, but it's **often acceptable**:
**Recommendation**: Prefer streams for readability. Optimize to loops only when profiling shows a bottleneck.
// ๐ด Stream created per iteration in hot loop
for (int i = 0; i < 1_000_000; i++) {
boolean found = items.stream()
.anyMatch(item -> item.getId() == i);
}
// โ
Pre-compute lookup structure
Set<Integer> itemIds = items.stream()
.map(Item::getId)
.collect(Collectors.toSet());
for (int i = 0; i < 1_000_000; i++) {
boolean found = itemIds.contains(i);
}// โ
Single pass, readable, not in tight loop
List<String> names = users.stream()
.filter(User::isActive)
.map(User::getName)
.sorted()
.collect(Collectors.toList());
// โ
Primitive streams avoid boxing
int sum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();// ๐ด Parallel on small collection - overhead > benefit
smallList.parallelStream().map(...); // < 10K items
// ๐ด Parallel with shared mutable state
List<String> results = new ArrayList<>();
items.parallelStream()
.forEach(results::add); // Race condition!
// โ
Parallel for CPU-intensive + large collections
List<Result> results = largeDataset.parallelStream() // > 10K items
.map(this::expensiveCpuComputation)
.collect(Collectors.toList());---
Boxing creates objects on heap, adds GC pressure. JVM caches small values (-128 to 127) but not larger ones.
> **Future**: Project Valhalla will improve this significantly.
// ๐ด Boxing in tight loop - creates millions of objects
Long sum = 0L;
for (int i = 0; i < 1_000_000; i++) {
sum += i; // Unbox, add, box
}
// โ
Primitive
long sum = 0L;
for (int i = 0; i < 1_000_000; i++) {
sum += i;
}// ๐ก Boxing overhead
int sum = list.stream()
.reduce(0, Integer::sum);
// โ
Primitive stream
int sum = list.stream()
.mapToInt(Integer::intValue)
.sum();---
This advice is **not outdated** - Pattern.compile is expensive.
// ๐ด Compiles pattern every iteration
for (String input : inputs) {
if (input.matches("\\d{3}-\\d{4}")) { // Compiles regex!
process(input);
}
}
// โ
Pre-compile
private static final Pattern PHONE = Pattern.compile("\\d{3}-\\d{4}");
for (String input : inputs) {
if (PHONE.matcher(input).matches()) {
process(input);
}
}---
// ๐ข Low severity - but free optimization if size known List<User> users = new ArrayList<>(expectedSize); Map<String, User> map = new Has
Agent Skills for Java projects, following the open Agent Skills specification This project is not affiliated with Anthropic.
Review REST API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when user asksโฆ
Analyze Java project architecture at macro level - package structure, module boundaries, dependency direction, and layering. Useโฆ
Generate changelogs from git commits. Use when user says "generate changelog", "update changelog", "what changed since lastโฆ
Clean Code principles (DRY, KISS, YAGNI), naming, function design and readability. Use when code is hard to read, with longโฆ
Review Java concurrency code for thread safety, race conditions, deadlocks, and modern patterns (Virtual Threads,โฆ
Common design patterns with Java examples (Factory, Builder, Strategy, Observer, Decorator, etc.). Use when user asks "implementโฆ
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, orโฆ
Ask which skill or flow fits your situation. A router over the skills in this repo.