| name | optimizing-code |
| description | Improve code performance without changing behavior. Use when code fails latency/throughput requirements. Covers profiling, caching, and algorithmic optimization. |
| allowed-tools | Read, Write, Edit, Bash, Glob, Grep |
Optimizing Code
The Optimization Hat
When optimizing, you improve performance without changing behavior. Always measure before and after.
Golden Rules
- Measure First: Never optimize without a benchmark
- Profile Before Guessing: Find the actual bottleneck
- Optimize the Right Thing: Focus on the critical path
- Measure After: Verify the optimization worked
Workflows
Common Optimizations
Algorithm Complexity
- Replace O(n²) with O(n log n) or O(n)
- Use appropriate data structures (Set for lookups, Map for key-value)
Caching (Java + Guava)
@Service
public class DataService {
private final LoadingCache<String, Result> cache;
public DataService() {
this.cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, Result>() {
@Override
public Result load(String key) {
return expensiveCalculation(key);
}
});
}
public Result getData(String input) {
return cache.getUnchecked(input);
}
private Result expensiveCalculation(String input) {
return new Result();
}
}
Virtual Threads (Java 24)
@Configuration
public class VirtualThreadConfig {
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutor() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Future<UserDto> user = scope.fork(() -> fetchUser(userId));
Future<List<Order>> orders = scope.fork(() -> fetchOrders(userId));
scope.join();
scope.throwIfFailed();
return buildProfile(user.resultNow(), orders.resultNow());
}
Database Queries (JPA/Hibernate)
@GetMapping("/users")
public List<UserDto> getUsers() {
List<User> users = userRepository.findAll();
return users.stream()
.map(user -> new UserDto(user, user.getOrders()))
.toList();
}
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.status = :status")
Page<User> findActiveUsersWithOrders(@Param("status") UserStatus status, Pageable pageable);
@EntityGraph(attributePaths = {"orders", "profile"})
List<User> findByStatus(UserStatus status);
@GetMapping("/users")
public Page<UserDto> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size
) {
Pageable pageable = PageRequest.of(page, size);
return userService.findAll(pageable);
}
- Add indexes for frequently queried columns
- Avoid N+1 queries (use JOIN FETCH or @EntityGraph)
- Use pagination for large result sets
- Use read-only transactions for queries:
@Transactional(readOnly = true)
Memory
- Avoid creating unnecessary objects in loops
- Use streaming for large files
- Release references when done
Profiling Tools
jvisualvm
java -agentpath:/path/to/libasyncProfiler.so=start,event=cpu,file=profile.html -jar app.jar
curl http://localhost:8081/actuator/metrics
mvn exec:java -Dexec.mainClass=org.openjdk.jmh.Main
jmap -dump:format=b,file=heap.bin <pid>
jhat heap.bin
jstack <pid> > threads.txt
java -Xlog:gc*:file=gc.log -jar app.jar
Anti-Patterns to Avoid
- Premature optimization (no benchmark)
- Micro-optimizations (negligible impact)
- Optimizing cold paths
- Sacrificing readability for minor gains