agentic-team-templates 0.13.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +6 -1
  2. package/package.json +1 -1
  3. package/src/index.js +91 -13
  4. package/src/index.test.js +95 -1
  5. package/templates/cpp-expert/.cursorrules/concurrency.md +211 -0
  6. package/templates/cpp-expert/.cursorrules/error-handling.md +170 -0
  7. package/templates/cpp-expert/.cursorrules/memory-and-ownership.md +220 -0
  8. package/templates/cpp-expert/.cursorrules/modern-cpp.md +211 -0
  9. package/templates/cpp-expert/.cursorrules/overview.md +87 -0
  10. package/templates/cpp-expert/.cursorrules/performance.md +223 -0
  11. package/templates/cpp-expert/.cursorrules/testing.md +230 -0
  12. package/templates/cpp-expert/.cursorrules/tooling.md +312 -0
  13. package/templates/cpp-expert/CLAUDE.md +242 -0
  14. package/templates/csharp-expert/.cursorrules/aspnet-core.md +311 -0
  15. package/templates/csharp-expert/.cursorrules/async-patterns.md +206 -0
  16. package/templates/csharp-expert/.cursorrules/dependency-injection.md +206 -0
  17. package/templates/csharp-expert/.cursorrules/error-handling.md +235 -0
  18. package/templates/csharp-expert/.cursorrules/language-features.md +204 -0
  19. package/templates/csharp-expert/.cursorrules/overview.md +92 -0
  20. package/templates/csharp-expert/.cursorrules/performance.md +251 -0
  21. package/templates/csharp-expert/.cursorrules/testing.md +282 -0
  22. package/templates/csharp-expert/.cursorrules/tooling.md +254 -0
  23. package/templates/csharp-expert/CLAUDE.md +360 -0
  24. package/templates/java-expert/.cursorrules/concurrency.md +209 -0
  25. package/templates/java-expert/.cursorrules/error-handling.md +205 -0
  26. package/templates/java-expert/.cursorrules/modern-java.md +216 -0
  27. package/templates/java-expert/.cursorrules/overview.md +81 -0
  28. package/templates/java-expert/.cursorrules/performance.md +239 -0
  29. package/templates/java-expert/.cursorrules/persistence.md +262 -0
  30. package/templates/java-expert/.cursorrules/spring-boot.md +262 -0
  31. package/templates/java-expert/.cursorrules/testing.md +272 -0
  32. package/templates/java-expert/.cursorrules/tooling.md +301 -0
  33. package/templates/java-expert/CLAUDE.md +325 -0
  34. package/templates/javascript-expert/.cursorrules/overview.md +5 -3
  35. package/templates/javascript-expert/.cursorrules/typescript-deep-dive.md +348 -0
  36. package/templates/javascript-expert/CLAUDE.md +34 -3
  37. package/templates/kotlin-expert/.cursorrules/coroutines.md +237 -0
  38. package/templates/kotlin-expert/.cursorrules/error-handling.md +149 -0
  39. package/templates/kotlin-expert/.cursorrules/frameworks.md +227 -0
  40. package/templates/kotlin-expert/.cursorrules/language-features.md +231 -0
  41. package/templates/kotlin-expert/.cursorrules/overview.md +77 -0
  42. package/templates/kotlin-expert/.cursorrules/performance.md +185 -0
  43. package/templates/kotlin-expert/.cursorrules/testing.md +213 -0
  44. package/templates/kotlin-expert/.cursorrules/tooling.md +258 -0
  45. package/templates/kotlin-expert/CLAUDE.md +276 -0
  46. package/templates/swift-expert/.cursorrules/concurrency.md +230 -0
  47. package/templates/swift-expert/.cursorrules/error-handling.md +213 -0
  48. package/templates/swift-expert/.cursorrules/language-features.md +246 -0
  49. package/templates/swift-expert/.cursorrules/overview.md +88 -0
  50. package/templates/swift-expert/.cursorrules/performance.md +260 -0
  51. package/templates/swift-expert/.cursorrules/swiftui.md +260 -0
  52. package/templates/swift-expert/.cursorrules/testing.md +286 -0
  53. package/templates/swift-expert/.cursorrules/tooling.md +285 -0
  54. package/templates/swift-expert/CLAUDE.md +275 -0
@@ -0,0 +1,209 @@
1
+ # Java Concurrency
2
+
3
+ The Java Memory Model is the law. Every concurrent decision must respect it.
4
+
5
+ ## Virtual Threads (Java 21+)
6
+
7
+ The default for I/O-bound work. Platform threads for CPU-bound work.
8
+
9
+ ```java
10
+ // Virtual threads for I/O concurrency
11
+ try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
12
+ var futures = tasks.stream()
13
+ .map(task -> executor.submit(() -> process(task)))
14
+ .toList();
15
+
16
+ var results = futures.stream()
17
+ .map(f -> {
18
+ try { return f.get(30, TimeUnit.SECONDS); }
19
+ catch (TimeoutException e) { throw new ProcessingTimeoutException(e); }
20
+ catch (ExecutionException e) { throw new ProcessingException(e.getCause()); }
21
+ catch (InterruptedException e) {
22
+ Thread.currentThread().interrupt();
23
+ throw new ProcessingException(e);
24
+ }
25
+ })
26
+ .toList();
27
+ }
28
+ ```
29
+
30
+ ### Virtual Thread Rules
31
+
32
+ - Don't pool virtual threads — they're cheap to create
33
+ - Don't use `synchronized` for I/O operations — use `ReentrantLock`
34
+ - Don't pin virtual threads to carrier threads (`synchronized` blocks pin)
35
+ - Don't use `ThreadLocal` for request-scoped data — use `ScopedValue` (preview)
36
+
37
+ ```java
38
+ // Bad: synchronized pins virtual thread to carrier
39
+ synchronized (lock) {
40
+ database.query(sql); // I/O while pinned — wastes carrier thread
41
+ }
42
+
43
+ // Good: ReentrantLock doesn't pin
44
+ private final ReentrantLock lock = new ReentrantLock();
45
+
46
+ lock.lock();
47
+ try {
48
+ database.query(sql); // Virtual thread can unmount during I/O
49
+ } finally {
50
+ lock.unlock();
51
+ }
52
+ ```
53
+
54
+ ## Structured Concurrency (Preview)
55
+
56
+ ```java
57
+ // All subtasks succeed or all are cancelled — no leaked threads
58
+ try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
59
+ Subtask<User> userTask = scope.fork(() -> userService.findById(userId));
60
+ Subtask<List<Order>> ordersTask = scope.fork(() -> orderService.findByUser(userId));
61
+
62
+ scope.join(); // Wait for all
63
+ scope.throwIfFailed(); // Propagate first failure
64
+
65
+ return new Dashboard(userTask.get(), ordersTask.get());
66
+ }
67
+ ```
68
+
69
+ ## CompletableFuture
70
+
71
+ ```java
72
+ // Async pipeline with proper error handling
73
+ public CompletableFuture<OrderResult> processOrderAsync(CreateOrderRequest request) {
74
+ return validateAsync(request)
75
+ .thenCompose(this::checkInventoryAsync)
76
+ .thenCompose(this::createOrderAsync)
77
+ .thenApply(OrderResult::success)
78
+ .exceptionally(ex -> {
79
+ log.error("Order processing failed", ex);
80
+ return OrderResult.failure(ex.getMessage());
81
+ });
82
+ }
83
+
84
+ // Combine independent operations
85
+ public CompletableFuture<Dashboard> loadDashboard(UUID userId) {
86
+ var userFuture = CompletableFuture.supplyAsync(() -> userService.findById(userId));
87
+ var ordersFuture = CompletableFuture.supplyAsync(() -> orderService.recent(userId));
88
+ var statsFuture = CompletableFuture.supplyAsync(() -> statsService.forUser(userId));
89
+
90
+ return CompletableFuture.allOf(userFuture, ordersFuture, statsFuture)
91
+ .thenApply(v -> new Dashboard(
92
+ userFuture.join(),
93
+ ordersFuture.join(),
94
+ statsFuture.join()));
95
+ }
96
+ ```
97
+
98
+ ## Thread Safety Patterns
99
+
100
+ ### Immutability (Preferred)
101
+
102
+ ```java
103
+ // Immutable objects are inherently thread-safe
104
+ public record UserSnapshot(UUID id, String name, Instant capturedAt) {}
105
+
106
+ // Unmodifiable collections
107
+ private final List<String> allowedRoles = List.of("USER", "ADMIN", "MODERATOR");
108
+ ```
109
+
110
+ ### Atomic Operations
111
+
112
+ ```java
113
+ private final AtomicLong requestCounter = new AtomicLong();
114
+ private final AtomicReference<Config> currentConfig = new AtomicReference<>(Config.defaults());
115
+
116
+ public void handleRequest() {
117
+ requestCounter.incrementAndGet();
118
+ }
119
+
120
+ public void updateConfig(Config newConfig) {
121
+ currentConfig.set(newConfig);
122
+ }
123
+ ```
124
+
125
+ ### ConcurrentHashMap
126
+
127
+ ```java
128
+ // Thread-safe map with atomic compute operations
129
+ private final ConcurrentHashMap<String, AtomicLong> counters = new ConcurrentHashMap<>();
130
+
131
+ public void increment(String key) {
132
+ counters.computeIfAbsent(key, k -> new AtomicLong()).incrementAndGet();
133
+ }
134
+
135
+ // Never iterate and modify — use compute/merge operations
136
+ counters.merge(key, new AtomicLong(1), (existing, value) -> {
137
+ existing.incrementAndGet();
138
+ return existing;
139
+ });
140
+ ```
141
+
142
+ ### Bounded Queues
143
+
144
+ ```java
145
+ // Backpressure via bounded queue
146
+ private final BlockingQueue<Event> eventQueue = new LinkedBlockingQueue<>(10_000);
147
+
148
+ public boolean publish(Event event) {
149
+ return eventQueue.offer(event); // Returns false if full — handle backpressure
150
+ }
151
+
152
+ public void consume() {
153
+ while (!Thread.currentThread().isInterrupted()) {
154
+ try {
155
+ Event event = eventQueue.poll(1, TimeUnit.SECONDS);
156
+ if (event != null) process(event);
157
+ } catch (InterruptedException e) {
158
+ Thread.currentThread().interrupt();
159
+ break;
160
+ }
161
+ }
162
+ }
163
+ ```
164
+
165
+ ## Interrupt Handling
166
+
167
+ ```java
168
+ // Always restore interrupt status
169
+ public void processItems(List<Item> items) {
170
+ for (var item : items) {
171
+ if (Thread.currentThread().isInterrupted()) {
172
+ log.warn("Processing interrupted, {} items remaining", items.size());
173
+ break;
174
+ }
175
+ process(item);
176
+ }
177
+ }
178
+
179
+ // In catch blocks
180
+ try {
181
+ Thread.sleep(Duration.ofSeconds(5));
182
+ } catch (InterruptedException e) {
183
+ Thread.currentThread().interrupt(); // Restore flag
184
+ throw new RuntimeException("Operation interrupted", e);
185
+ }
186
+ ```
187
+
188
+ ## Anti-Patterns
189
+
190
+ ```java
191
+ // Never: double-checked locking without volatile (pre-Java 5 bug, but still seen)
192
+ // Use: enum singleton, or lazy holder pattern, or just @Singleton
193
+
194
+ // Never: synchronized on non-final fields
195
+ private Object lock = new Object(); // Can be reassigned!
196
+ synchronized (lock) { } // Different threads may lock different objects
197
+ // Use: private final Object lock = new Object();
198
+
199
+ // Never: Thread.stop() or Thread.suspend()
200
+ // Use: cooperative interruption via Thread.interrupt()
201
+
202
+ // Never: busy-wait
203
+ while (!ready) { } // Burns CPU
204
+ // Use: CountDownLatch, Semaphore, or BlockingQueue
205
+
206
+ // Never: shared mutable state without synchronization
207
+ private int counter; // Multiple threads read/write without coordination
208
+ // Use: AtomicInteger, or synchronize access
209
+ ```
@@ -0,0 +1,205 @@
1
+ # Java Error Handling
2
+
3
+ Exceptions for exceptional conditions. Validation at boundaries. Never swallow errors.
4
+
5
+ ## Exception Hierarchy
6
+
7
+ ```java
8
+ // Application exception base — unchecked
9
+ public abstract class ApplicationException extends RuntimeException {
10
+ private final String errorCode;
11
+
12
+ protected ApplicationException(String errorCode, String message) {
13
+ super(message);
14
+ this.errorCode = errorCode;
15
+ }
16
+
17
+ protected ApplicationException(String errorCode, String message, Throwable cause) {
18
+ super(message, cause);
19
+ this.errorCode = errorCode;
20
+ }
21
+
22
+ public String getErrorCode() { return errorCode; }
23
+ }
24
+
25
+ // Specific exceptions
26
+ public class NotFoundException extends ApplicationException {
27
+ public NotFoundException(String entity, Object id) {
28
+ super("NOT_FOUND", "%s with id '%s' not found".formatted(entity, id));
29
+ }
30
+ }
31
+
32
+ public class ConflictException extends ApplicationException {
33
+ public ConflictException(String message) {
34
+ super("CONFLICT", message);
35
+ }
36
+ }
37
+
38
+ public class ValidationException extends ApplicationException {
39
+ private final Map<String, String> fieldErrors;
40
+
41
+ public ValidationException(Map<String, String> fieldErrors) {
42
+ super("VALIDATION_FAILED", "Validation failed");
43
+ this.fieldErrors = Map.copyOf(fieldErrors);
44
+ }
45
+
46
+ public Map<String, String> getFieldErrors() { return fieldErrors; }
47
+ }
48
+ ```
49
+
50
+ ## Checked vs Unchecked
51
+
52
+ ```java
53
+ // Checked exceptions: recoverable conditions at system boundaries
54
+ // - IOException: file not found, network error — caller can retry or use fallback
55
+ // - SQLException: database error — caller can retry with backoff
56
+
57
+ // Unchecked exceptions: programming errors and business rule violations
58
+ // - IllegalArgumentException: bad input
59
+ // - NullPointerException: null where not expected
60
+ // - NotFoundException: domain-level "not found"
61
+ // - ValidationException: business rule violation
62
+
63
+ // Rule: if the caller CAN and SHOULD handle it → checked
64
+ // if it's a bug or unrecoverable → unchecked
65
+ ```
66
+
67
+ ## Validation
68
+
69
+ ```java
70
+ // Jakarta Bean Validation on DTOs
71
+ public record CreateUserRequest(
72
+ @NotBlank(message = "Name is required")
73
+ @Size(min = 2, max = 200, message = "Name must be 2-200 characters")
74
+ String name,
75
+
76
+ @NotBlank(message = "Email is required")
77
+ @Email(message = "Invalid email format")
78
+ String email,
79
+
80
+ @NotNull(message = "Role is required")
81
+ UserRole role
82
+ ) {}
83
+
84
+ // Custom validator
85
+ @Constraint(validatedBy = UniqueEmailValidator.class)
86
+ @Target(ElementType.FIELD)
87
+ @Retention(RetentionPolicy.RUNTIME)
88
+ public @interface UniqueEmail {
89
+ String message() default "Email already registered";
90
+ Class<?>[] groups() default {};
91
+ Class<? extends Payload>[] payload() default {};
92
+ }
93
+
94
+ public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
95
+ private final UserRepository userRepository;
96
+
97
+ @Override
98
+ public boolean isValid(String email, ConstraintValidatorContext context) {
99
+ return email == null || !userRepository.existsByEmail(email);
100
+ }
101
+ }
102
+ ```
103
+
104
+ ## Guard Clauses
105
+
106
+ ```java
107
+ public class OrderService {
108
+
109
+ public Order create(String customerId, List<OrderItem> items) {
110
+ // Fail fast with meaningful messages
111
+ Objects.requireNonNull(customerId, "customerId must not be null");
112
+ if (customerId.isBlank()) {
113
+ throw new IllegalArgumentException("customerId must not be blank");
114
+ }
115
+ if (items == null || items.isEmpty()) {
116
+ throw new IllegalArgumentException("Order must have at least one item");
117
+ }
118
+ if (items.size() > MAX_ITEMS) {
119
+ throw new ValidationException(Map.of(
120
+ "items", "Maximum %d items allowed".formatted(MAX_ITEMS)));
121
+ }
122
+
123
+ // Business logic follows clean preconditions
124
+ return Order.create(customerId, items);
125
+ }
126
+ }
127
+ ```
128
+
129
+ ## Result Pattern (Alternative to Exceptions)
130
+
131
+ ```java
132
+ // For operations where failure is expected, not exceptional
133
+ public sealed interface Result<T> permits Result.Success, Result.Failure {
134
+
135
+ record Success<T>(T value) implements Result<T> {}
136
+ record Failure<T>(String code, String message) implements Result<T> {}
137
+
138
+ static <T> Result<T> success(T value) { return new Success<>(value); }
139
+ static <T> Result<T> failure(String code, String message) { return new Failure<>(code, message); }
140
+
141
+ default <U> U match(Function<T, U> onSuccess, BiFunction<String, String, U> onFailure) {
142
+ return switch (this) {
143
+ case Success<T> s -> onSuccess.apply(s.value());
144
+ case Failure<T> f -> onFailure.apply(f.code(), f.message());
145
+ };
146
+ }
147
+ }
148
+
149
+ // Usage
150
+ public Result<User> register(RegisterRequest request) {
151
+ if (userRepository.existsByEmail(request.email())) {
152
+ return Result.failure("CONFLICT", "Email already registered");
153
+ }
154
+ var user = User.create(request.name(), request.email());
155
+ userRepository.save(user);
156
+ return Result.success(user);
157
+ }
158
+ ```
159
+
160
+ ## Logging Exceptions
161
+
162
+ ```java
163
+ // Log with context, not just the exception
164
+ try {
165
+ return paymentService.charge(orderId, amount);
166
+ } catch (PaymentException ex) {
167
+ log.error("Payment failed for order {} amount {}: {}",
168
+ orderId, amount, ex.getMessage(), ex);
169
+ throw new OrderProcessingException("Payment failed for order " + orderId, ex);
170
+ }
171
+
172
+ // Rules:
173
+ // - Log at the point of handling, not at every rethrow
174
+ // - Include business context (IDs, amounts, actions)
175
+ // - Use structured logging parameters, not string concatenation
176
+ // - Include the exception as the LAST parameter (for stack trace)
177
+ // - Don't log AND throw at the same level (choose one)
178
+ ```
179
+
180
+ ## Anti-Patterns
181
+
182
+ ```java
183
+ // Never: catching Exception or Throwable broadly
184
+ try { doWork(); }
185
+ catch (Exception e) { log.error("Error", e); }
186
+ // Catches InterruptedException, NPE, and everything else — too broad
187
+
188
+ // Never: empty catch blocks
189
+ try { connection.close(); }
190
+ catch (Exception e) { /* ignore */ }
191
+ // At minimum: log.debug("Error closing connection", e);
192
+
193
+ // Never: using exceptions for control flow
194
+ try { return Integer.parseInt(input); }
195
+ catch (NumberFormatException e) { return defaultValue; }
196
+ // Use: input.matches("\\d+") or a tryParse utility
197
+
198
+ // Never: wrapping without adding context
199
+ catch (SQLException e) { throw new RuntimeException(e); }
200
+ // Add context: throw new PersistenceException("Failed to save order " + orderId, e);
201
+
202
+ // Never: losing the original exception
203
+ catch (Exception e) { throw new RuntimeException("Something failed"); }
204
+ // Always chain: throw new RuntimeException("Something failed", e);
205
+ ```
@@ -0,0 +1,216 @@
1
+ # Modern Java Language Features
2
+
3
+ Java 21+ features used deliberately. Every feature exists to make code clearer — not to show off.
4
+
5
+ ## Records
6
+
7
+ ```java
8
+ // Immutable data carriers — the default for DTOs, value objects, events
9
+ public record CreateUserRequest(
10
+ @NotBlank String name,
11
+ @Email String email
12
+ ) {}
13
+
14
+ public record Money(BigDecimal amount, Currency currency) {
15
+ // Compact constructor for validation
16
+ public Money {
17
+ if (amount.compareTo(BigDecimal.ZERO) < 0) {
18
+ throw new IllegalArgumentException("Amount cannot be negative");
19
+ }
20
+ Objects.requireNonNull(currency, "Currency is required");
21
+ }
22
+ }
23
+
24
+ public record PageRequest(int page, int size) {
25
+ public PageRequest {
26
+ if (page < 0) throw new IllegalArgumentException("Page must be >= 0");
27
+ if (size < 1 || size > 100) throw new IllegalArgumentException("Size must be 1-100");
28
+ }
29
+
30
+ public int offset() {
31
+ return page * size;
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### When NOT to Use Records
37
+
38
+ - JPA entities (need mutable state, no-arg constructor)
39
+ - Spring beans (need proxying, lifecycle management)
40
+ - Classes with complex behavior beyond data carrying
41
+
42
+ ## Sealed Classes
43
+
44
+ ```java
45
+ // Exhaustive type hierarchies — the compiler enforces completeness
46
+ public sealed interface PaymentResult permits PaymentSuccess, PaymentFailure, PaymentPending {
47
+ }
48
+
49
+ public record PaymentSuccess(String transactionId, Instant processedAt) implements PaymentResult {}
50
+ public record PaymentFailure(String errorCode, String message) implements PaymentResult {}
51
+ public record PaymentPending(String referenceId, Duration estimatedWait) implements PaymentResult {}
52
+
53
+ // Exhaustive switch — compiler warns if a case is missing
54
+ public String describeResult(PaymentResult result) {
55
+ return switch (result) {
56
+ case PaymentSuccess s -> "Paid: " + s.transactionId();
57
+ case PaymentFailure f -> "Failed: " + f.message();
58
+ case PaymentPending p -> "Pending: " + p.referenceId();
59
+ };
60
+ }
61
+ ```
62
+
63
+ ## Pattern Matching
64
+
65
+ ```java
66
+ // instanceof pattern matching — no more casting
67
+ public String format(Object value) {
68
+ return switch (value) {
69
+ case Integer i when i < 0 -> "negative: " + i;
70
+ case Integer i -> "integer: " + i;
71
+ case String s when s.isEmpty() -> "empty string";
72
+ case String s -> "string: " + s;
73
+ case null -> "null";
74
+ default -> "unknown: " + value.getClass().getSimpleName();
75
+ };
76
+ }
77
+
78
+ // Record patterns (Java 21+)
79
+ public double calculateArea(Shape shape) {
80
+ return switch (shape) {
81
+ case Circle(double radius) -> Math.PI * radius * radius;
82
+ case Rectangle(double w, double h) -> w * h;
83
+ case Triangle(double base, double height) -> 0.5 * base * height;
84
+ };
85
+ }
86
+ ```
87
+
88
+ ## Text Blocks
89
+
90
+ ```java
91
+ // Multi-line strings with proper indentation
92
+ String query = """
93
+ SELECT u.id, u.name, u.email
94
+ FROM users u
95
+ WHERE u.active = true
96
+ AND u.created_at > :since
97
+ ORDER BY u.name
98
+ """;
99
+
100
+ String json = """
101
+ {
102
+ "name": "%s",
103
+ "email": "%s"
104
+ }
105
+ """.formatted(name, email);
106
+ ```
107
+
108
+ ## Optional
109
+
110
+ ```java
111
+ // Return type for "might not exist" — never for fields or parameters
112
+ public Optional<User> findByEmail(String email) {
113
+ return Optional.ofNullable(userRepository.findByEmail(email));
114
+ }
115
+
116
+ // Good: chain operations
117
+ public String getUserDisplayName(String email) {
118
+ return findByEmail(email)
119
+ .map(User::displayName)
120
+ .orElse("Unknown User");
121
+ }
122
+
123
+ // Good: throw with context
124
+ public User getByEmail(String email) {
125
+ return findByEmail(email)
126
+ .orElseThrow(() -> new NotFoundException("User not found: " + email));
127
+ }
128
+
129
+ // Bad: Optional.get() without check — defeats the purpose
130
+ user.get().getName(); // NoSuchElementException risk
131
+
132
+ // Bad: Optional as method parameter
133
+ public void process(Optional<String> name) { } // Use @Nullable instead
134
+
135
+ // Bad: Optional as field
136
+ private Optional<String> middleName; // Use @Nullable instead
137
+ ```
138
+
139
+ ## Collections
140
+
141
+ ```java
142
+ // Immutable collections — the default
143
+ var users = List.of("Alice", "Bob", "Charlie");
144
+ var scores = Map.of("Alice", 95, "Bob", 87);
145
+ var uniqueNames = Set.of("Alice", "Bob");
146
+
147
+ // List.copyOf, Set.copyOf, Map.copyOf for defensive copies
148
+ public List<User> getUsers() {
149
+ return List.copyOf(mutableUserList); // Defensive copy
150
+ }
151
+
152
+ // Collectors and Stream API
153
+ var activeUsersByDepartment = users.stream()
154
+ .filter(User::isActive)
155
+ .collect(Collectors.groupingBy(
156
+ User::department,
157
+ Collectors.toUnmodifiableList()));
158
+
159
+ // toList() shorthand (Java 16+)
160
+ var names = users.stream()
161
+ .map(User::name)
162
+ .toList(); // Returns unmodifiable list
163
+ ```
164
+
165
+ ## Virtual Threads (Java 21+)
166
+
167
+ ```java
168
+ // Virtual threads for I/O-bound concurrency — massive scalability
169
+ try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
170
+ List<Future<Response>> futures = urls.stream()
171
+ .map(url -> executor.submit(() -> httpClient.send(url)))
172
+ .toList();
173
+
174
+ List<Response> responses = futures.stream()
175
+ .map(f -> {
176
+ try { return f.get(); }
177
+ catch (Exception e) { throw new RuntimeException(e); }
178
+ })
179
+ .toList();
180
+ }
181
+
182
+ // Spring Boot 3.2+: enable virtual threads
183
+ // spring.threads.virtual.enabled=true
184
+
185
+ // Rules for virtual threads:
186
+ // - Don't pool them (they're cheap to create)
187
+ // - Don't use synchronized blocks for I/O (use ReentrantLock)
188
+ // - Don't use ThreadLocal for request-scoped data (use ScopedValue)
189
+ // - Perfect for: HTTP handlers, database queries, external API calls
190
+ // - Not for: CPU-bound computation (use platform threads / ForkJoinPool)
191
+ ```
192
+
193
+ ## Anti-Patterns
194
+
195
+ ```java
196
+ // Never: raw types
197
+ List list = new ArrayList(); // What's in it?
198
+ // Use: List<User> users = new ArrayList<>();
199
+
200
+ // Never: checked exceptions for control flow
201
+ try { return Integer.parseInt(input); }
202
+ catch (NumberFormatException e) { return -1; }
203
+ // Use validation before parsing
204
+
205
+ // Never: mutable public fields
206
+ public class Config {
207
+ public String url; // Anyone can change this
208
+ }
209
+ // Use: records, or private fields with getters
210
+
211
+ // Never: null as a valid business value
212
+ public User findUser(String id) {
213
+ return null; // Caller forgets null check → NPE
214
+ }
215
+ // Use: Optional<User> or throw NotFoundException
216
+ ```