red64-cli 0.3.0 → 0.6.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.
- package/README.md +194 -338
- package/dist/cli/parseArgs.d.ts.map +1 -1
- package/dist/cli/parseArgs.js +5 -13
- package/dist/cli/parseArgs.js.map +1 -1
- package/dist/components/init/types.d.ts +0 -2
- package/dist/components/init/types.d.ts.map +1 -1
- package/dist/components/screens/HelpScreen.d.ts.map +1 -1
- package/dist/components/screens/HelpScreen.js +0 -2
- package/dist/components/screens/HelpScreen.js.map +1 -1
- package/dist/components/screens/InitScreen.d.ts.map +1 -1
- package/dist/components/screens/InitScreen.js +5 -8
- package/dist/components/screens/InitScreen.js.map +1 -1
- package/dist/components/screens/StartScreen.d.ts.map +1 -1
- package/dist/components/screens/StartScreen.js +29 -8
- package/dist/components/screens/StartScreen.js.map +1 -1
- package/dist/components/screens/StatusScreen.d.ts.map +1 -1
- package/dist/components/screens/StatusScreen.js +16 -1
- package/dist/components/screens/StatusScreen.js.map +1 -1
- package/dist/services/AgentInvoker.d.ts.map +1 -1
- package/dist/services/AgentInvoker.js +76 -37
- package/dist/services/AgentInvoker.js.map +1 -1
- package/dist/services/ClaudeErrorDetector.d.ts +1 -1
- package/dist/services/ClaudeErrorDetector.d.ts.map +1 -1
- package/dist/services/ClaudeErrorDetector.js +1 -0
- package/dist/services/ClaudeErrorDetector.js.map +1 -1
- package/dist/services/ClaudeHealthCheck.d.ts +7 -0
- package/dist/services/ClaudeHealthCheck.d.ts.map +1 -1
- package/dist/services/ClaudeHealthCheck.js +76 -12
- package/dist/services/ClaudeHealthCheck.js.map +1 -1
- package/dist/services/ConfigService.d.ts +1 -0
- package/dist/services/ConfigService.d.ts.map +1 -1
- package/dist/services/ConfigService.js.map +1 -1
- package/dist/services/DockerRunner.js +1 -1
- package/dist/services/DockerRunner.js.map +1 -1
- package/dist/services/PhaseExecutor.d.ts.map +1 -1
- package/dist/services/PhaseExecutor.js +2 -1
- package/dist/services/PhaseExecutor.js.map +1 -1
- package/dist/services/TaskRunner.d.ts.map +1 -1
- package/dist/services/TaskRunner.js +2 -1
- package/dist/services/TaskRunner.js.map +1 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/framework/stacks/c/code-quality.md +326 -0
- package/framework/stacks/c/coding-style.md +347 -0
- package/framework/stacks/c/conventions.md +513 -0
- package/framework/stacks/c/error-handling.md +350 -0
- package/framework/stacks/c/feedback.md +158 -0
- package/framework/stacks/c/memory-safety.md +408 -0
- package/framework/stacks/c/tech.md +122 -0
- package/framework/stacks/c/testing.md +472 -0
- package/framework/stacks/cpp/code-quality.md +282 -0
- package/framework/stacks/cpp/coding-style.md +363 -0
- package/framework/stacks/cpp/conventions.md +420 -0
- package/framework/stacks/cpp/error-handling.md +264 -0
- package/framework/stacks/cpp/feedback.md +104 -0
- package/framework/stacks/cpp/memory-safety.md +351 -0
- package/framework/stacks/cpp/tech.md +160 -0
- package/framework/stacks/cpp/testing.md +323 -0
- package/framework/stacks/java/code-quality.md +357 -0
- package/framework/stacks/java/coding-style.md +400 -0
- package/framework/stacks/java/conventions.md +437 -0
- package/framework/stacks/java/error-handling.md +408 -0
- package/framework/stacks/java/feedback.md +180 -0
- package/framework/stacks/java/tech.md +126 -0
- package/framework/stacks/java/testing.md +485 -0
- package/framework/stacks/javascript/async-patterns.md +216 -0
- package/framework/stacks/javascript/code-quality.md +182 -0
- package/framework/stacks/javascript/coding-style.md +293 -0
- package/framework/stacks/javascript/conventions.md +268 -0
- package/framework/stacks/javascript/error-handling.md +216 -0
- package/framework/stacks/javascript/feedback.md +80 -0
- package/framework/stacks/javascript/tech.md +114 -0
- package/framework/stacks/javascript/testing.md +209 -0
- package/framework/stacks/loco/code-quality.md +156 -0
- package/framework/stacks/loco/coding-style.md +247 -0
- package/framework/stacks/loco/error-handling.md +225 -0
- package/framework/stacks/loco/feedback.md +35 -0
- package/framework/stacks/loco/loco.md +342 -0
- package/framework/stacks/loco/structure.md +193 -0
- package/framework/stacks/loco/tech.md +129 -0
- package/framework/stacks/loco/testing.md +211 -0
- package/framework/stacks/rust/code-quality.md +370 -0
- package/framework/stacks/rust/coding-style.md +475 -0
- package/framework/stacks/rust/conventions.md +430 -0
- package/framework/stacks/rust/error-handling.md +399 -0
- package/framework/stacks/rust/feedback.md +152 -0
- package/framework/stacks/rust/memory-safety.md +398 -0
- package/framework/stacks/rust/tech.md +121 -0
- package/framework/stacks/rust/testing.md +528 -0
- package/package.json +14 -2
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# Java Coding Style
|
|
2
|
+
|
|
3
|
+
Coding style conventions for modern Java 21+ projects. Opinionated patterns for readable, maintainable code leveraging records, sealed classes, pattern matching, and functional idioms.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Philosophy
|
|
8
|
+
|
|
9
|
+
- **Modern Java first**: Use records, sealed interfaces, pattern matching, and `var` -- write Java 21, not Java 8
|
|
10
|
+
- **Composition over inheritance**: Favor small, focused interfaces and delegation over deep class hierarchies
|
|
11
|
+
- **Immutability by default**: Records for data, unmodifiable collections, final fields
|
|
12
|
+
- **Explicit over clever**: Clear code beats terse code; optimize for the reader, not the writer
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Naming Conventions
|
|
17
|
+
|
|
18
|
+
### Standard Java Naming
|
|
19
|
+
|
|
20
|
+
| Element | Convention | Example |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| Classes, interfaces, enums, records | `PascalCase` | `UserService`, `CreateUserRequest` |
|
|
23
|
+
| Methods, local variables, parameters | `camelCase` | `getUserById`, `pageSize` |
|
|
24
|
+
| Constants (`static final`) | `UPPER_SNAKE_CASE` | `MAX_RETRIES`, `DEFAULT_PAGE_SIZE` |
|
|
25
|
+
| Packages | `lowercase.dotted` | `com.example.user.service` |
|
|
26
|
+
| Type parameters | Single uppercase letter or `PascalCase` | `T`, `E`, `ResponseT` |
|
|
27
|
+
| Boolean methods/variables | `is`/`has`/`can`/`should` prefix | `isActive()`, `hasPermission` |
|
|
28
|
+
|
|
29
|
+
### Naming Rules
|
|
30
|
+
|
|
31
|
+
```java
|
|
32
|
+
// GOOD: Descriptive, reveals intent
|
|
33
|
+
int activeUserCount = userRepository.countByActive(true);
|
|
34
|
+
boolean isAuthenticated = token != null && !token.isExpired();
|
|
35
|
+
var maxRetryAttempts = 3;
|
|
36
|
+
|
|
37
|
+
List<User> findActiveUsersByRole(Role role) { ... }
|
|
38
|
+
|
|
39
|
+
record CreateUserRequest(String email, String name) {}
|
|
40
|
+
|
|
41
|
+
// BAD: Abbreviated, unclear
|
|
42
|
+
int uc = repo.cnt(true);
|
|
43
|
+
boolean auth = t != null;
|
|
44
|
+
var n = 3;
|
|
45
|
+
|
|
46
|
+
List<User> getAU(Role r) { ... }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Boolean Naming
|
|
50
|
+
|
|
51
|
+
```java
|
|
52
|
+
// GOOD: Clear boolean intent
|
|
53
|
+
boolean isActive;
|
|
54
|
+
boolean hasPermission;
|
|
55
|
+
boolean canPublish;
|
|
56
|
+
boolean shouldNotify;
|
|
57
|
+
|
|
58
|
+
// Methods
|
|
59
|
+
boolean isEligibleForPromotion(Employee employee) { ... }
|
|
60
|
+
boolean hasAccessTo(Resource resource, User user) { ... }
|
|
61
|
+
|
|
62
|
+
// BAD: Ambiguous
|
|
63
|
+
boolean active; // is it a flag or a count?
|
|
64
|
+
boolean check; // check what?
|
|
65
|
+
boolean flag; // meaningless
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Modern Java Patterns
|
|
71
|
+
|
|
72
|
+
### Records for DTOs and Value Objects
|
|
73
|
+
|
|
74
|
+
Records replace boilerplate POJOs for immutable data carriers:
|
|
75
|
+
|
|
76
|
+
```java
|
|
77
|
+
// GOOD: Record for request/response DTOs
|
|
78
|
+
public record CreateUserRequest(
|
|
79
|
+
@NotBlank String email,
|
|
80
|
+
@NotBlank String name,
|
|
81
|
+
@Size(min = 8) String password
|
|
82
|
+
) {}
|
|
83
|
+
|
|
84
|
+
public record UserResponse(
|
|
85
|
+
Long id,
|
|
86
|
+
String email,
|
|
87
|
+
String name,
|
|
88
|
+
Instant createdAt
|
|
89
|
+
) {
|
|
90
|
+
// Compact constructor for validation
|
|
91
|
+
public UserResponse {
|
|
92
|
+
Objects.requireNonNull(email, "email must not be null");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Factory method from entity
|
|
96
|
+
public static UserResponse from(User user) {
|
|
97
|
+
return new UserResponse(user.getId(), user.getEmail(), user.getName(), user.getCreatedAt());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// BAD: Mutable POJO for data that should be immutable
|
|
102
|
+
public class CreateUserRequest {
|
|
103
|
+
private String email;
|
|
104
|
+
private String name;
|
|
105
|
+
private String password;
|
|
106
|
+
// 30+ lines of getters, setters, equals, hashCode, toString...
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Sealed Interfaces
|
|
111
|
+
|
|
112
|
+
Use sealed interfaces for closed type hierarchies:
|
|
113
|
+
|
|
114
|
+
```java
|
|
115
|
+
// GOOD: Sealed interface with exhaustive pattern matching
|
|
116
|
+
public sealed interface PaymentResult
|
|
117
|
+
permits PaymentResult.Success, PaymentResult.Declined, PaymentResult.Error {
|
|
118
|
+
|
|
119
|
+
record Success(String transactionId, long amountCents) implements PaymentResult {}
|
|
120
|
+
record Declined(String reason) implements PaymentResult {}
|
|
121
|
+
record Error(String message, Exception cause) implements PaymentResult {}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Exhaustive switch -- compiler enforces all cases
|
|
125
|
+
String describe(PaymentResult result) {
|
|
126
|
+
return switch (result) {
|
|
127
|
+
case PaymentResult.Success s -> "Paid: " + s.transactionId();
|
|
128
|
+
case PaymentResult.Declined d -> "Declined: " + d.reason();
|
|
129
|
+
case PaymentResult.Error e -> "Error: " + e.message();
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// BAD: Open class hierarchy with instanceof chains
|
|
134
|
+
if (result instanceof SuccessResult) { ... }
|
|
135
|
+
else if (result instanceof DeclinedResult) { ... }
|
|
136
|
+
else if (result instanceof ErrorResult) { ... }
|
|
137
|
+
else { /* easy to forget new subtypes */ }
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Pattern Matching
|
|
141
|
+
|
|
142
|
+
```java
|
|
143
|
+
// GOOD: Pattern matching for instanceof (Java 16+)
|
|
144
|
+
if (shape instanceof Circle c) {
|
|
145
|
+
return Math.PI * c.radius() * c.radius();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// GOOD: Switch expressions with pattern matching (Java 21+)
|
|
149
|
+
double area(Shape shape) {
|
|
150
|
+
return switch (shape) {
|
|
151
|
+
case Circle c -> Math.PI * c.radius() * c.radius();
|
|
152
|
+
case Rectangle r -> r.width() * r.height();
|
|
153
|
+
case Triangle t -> 0.5 * t.base() * t.height();
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// GOOD: Guarded patterns
|
|
158
|
+
String classify(Integer value) {
|
|
159
|
+
return switch (value) {
|
|
160
|
+
case Integer i when i < 0 -> "negative";
|
|
161
|
+
case Integer i when i == 0 -> "zero";
|
|
162
|
+
case Integer i -> "positive";
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// BAD: Old-style instanceof with cast
|
|
167
|
+
if (shape instanceof Circle) {
|
|
168
|
+
Circle c = (Circle) shape;
|
|
169
|
+
return Math.PI * c.radius() * c.radius();
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `var` for Local Variables
|
|
174
|
+
|
|
175
|
+
```java
|
|
176
|
+
// GOOD: var when type is obvious from context
|
|
177
|
+
var users = userRepository.findAll(); // clearly List<User>
|
|
178
|
+
var response = new UserResponse(1L, "a@b.com", "Alice", Instant.now());
|
|
179
|
+
var mapper = new ObjectMapper();
|
|
180
|
+
var entry = Map.entry("key", "value");
|
|
181
|
+
|
|
182
|
+
// GOOD: var in for-loops and try-with-resources
|
|
183
|
+
for (var user : users) { ... }
|
|
184
|
+
try (var stream = Files.lines(path)) { ... }
|
|
185
|
+
|
|
186
|
+
// BAD: var when type is not obvious
|
|
187
|
+
var result = process(data); // what type is result?
|
|
188
|
+
var x = calculateMetric(); // meaningless name + unclear type
|
|
189
|
+
|
|
190
|
+
// BAD: var for primitives where literal type matters
|
|
191
|
+
var timeout = 30; // int? long? -- be explicit
|
|
192
|
+
long timeoutMs = 30_000L; // clear
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Optional Usage
|
|
198
|
+
|
|
199
|
+
### Rules
|
|
200
|
+
|
|
201
|
+
```java
|
|
202
|
+
// GOOD: Optional as return type for "may not exist"
|
|
203
|
+
Optional<User> findByEmail(String email) { ... }
|
|
204
|
+
|
|
205
|
+
// GOOD: Use map/flatMap/orElseThrow -- never get() directly
|
|
206
|
+
User user = userRepository.findByEmail(email)
|
|
207
|
+
.orElseThrow(() -> new UserNotFoundException(email));
|
|
208
|
+
|
|
209
|
+
String displayName = userRepository.findById(id)
|
|
210
|
+
.map(User::getName)
|
|
211
|
+
.orElse("Unknown");
|
|
212
|
+
|
|
213
|
+
// BAD: Optional as field or parameter
|
|
214
|
+
public class UserService {
|
|
215
|
+
private Optional<Cache> cache; // use @Nullable or overload
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
void createUser(Optional<String> nickname) { ... } // just use @Nullable
|
|
219
|
+
|
|
220
|
+
// BAD: Calling get() without isPresent()
|
|
221
|
+
User user = findByEmail(email).get(); // NoSuchElementException risk
|
|
222
|
+
|
|
223
|
+
// BAD: Using Optional just to avoid null check
|
|
224
|
+
Optional.ofNullable(name).ifPresent(n -> setName(n)); // just use if (name != null)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Optional Method Chain Patterns
|
|
228
|
+
|
|
229
|
+
```java
|
|
230
|
+
// GOOD: Chained operations
|
|
231
|
+
String city = user.getAddress() // returns Optional<Address>
|
|
232
|
+
.map(Address::getCity)
|
|
233
|
+
.filter(c -> !c.isBlank())
|
|
234
|
+
.orElse("Unknown");
|
|
235
|
+
|
|
236
|
+
// GOOD: orElseGet for expensive defaults
|
|
237
|
+
User user = cache.get(id)
|
|
238
|
+
.orElseGet(() -> repository.findById(id)
|
|
239
|
+
.orElseThrow(() -> new NotFoundException("User", id)));
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Stream API Patterns
|
|
245
|
+
|
|
246
|
+
```java
|
|
247
|
+
// GOOD: Clean stream pipelines
|
|
248
|
+
List<String> activeEmails = users.stream()
|
|
249
|
+
.filter(User::isActive)
|
|
250
|
+
.map(User::getEmail)
|
|
251
|
+
.sorted()
|
|
252
|
+
.toList(); // Java 16+ immutable list
|
|
253
|
+
|
|
254
|
+
Map<Role, List<User>> usersByRole = users.stream()
|
|
255
|
+
.collect(Collectors.groupingBy(User::getRole));
|
|
256
|
+
|
|
257
|
+
// GOOD: Parallel streams for CPU-bound work on large collections
|
|
258
|
+
long total = largeDataSet.parallelStream()
|
|
259
|
+
.mapToLong(Item::getValue)
|
|
260
|
+
.sum();
|
|
261
|
+
|
|
262
|
+
// BAD: Side-effectful streams
|
|
263
|
+
users.stream()
|
|
264
|
+
.forEach(u -> u.setActive(false)); // mutating in stream -- use for-loop
|
|
265
|
+
|
|
266
|
+
// BAD: Overly complex stream when a loop is clearer
|
|
267
|
+
var result = items.stream()
|
|
268
|
+
.flatMap(i -> i.getChildren().stream())
|
|
269
|
+
.filter(c -> c.getType() == Type.A)
|
|
270
|
+
.collect(Collectors.groupingBy(
|
|
271
|
+
Child::getCategory,
|
|
272
|
+
Collectors.mapping(Child::getName, Collectors.joining(", "))
|
|
273
|
+
));
|
|
274
|
+
// If the pipeline exceeds 5 operations, consider extracting helper methods
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Composition Over Inheritance
|
|
280
|
+
|
|
281
|
+
```java
|
|
282
|
+
// GOOD: Composition with interfaces
|
|
283
|
+
public interface Validator<T> {
|
|
284
|
+
ValidationResult validate(T input);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public class UserValidator implements Validator<CreateUserRequest> {
|
|
288
|
+
private final List<Validator<CreateUserRequest>> rules;
|
|
289
|
+
|
|
290
|
+
public UserValidator(List<Validator<CreateUserRequest>> rules) {
|
|
291
|
+
this.rules = rules;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
@Override
|
|
295
|
+
public ValidationResult validate(CreateUserRequest input) {
|
|
296
|
+
return rules.stream()
|
|
297
|
+
.map(rule -> rule.validate(input))
|
|
298
|
+
.reduce(ValidationResult.ok(), ValidationResult::merge);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// BAD: Deep inheritance for code reuse
|
|
303
|
+
class BaseRepository<T> { ... }
|
|
304
|
+
class CachedRepository<T> extends BaseRepository<T> { ... }
|
|
305
|
+
class AuditedCachedRepository<T> extends CachedRepository<T> { ... }
|
|
306
|
+
class UserRepository extends AuditedCachedRepository<User> { ... }
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Function Design
|
|
312
|
+
|
|
313
|
+
### Size Limits
|
|
314
|
+
|
|
315
|
+
| Element | Guideline |
|
|
316
|
+
|---|---|
|
|
317
|
+
| Method body | Under 20 lines of logic, max 40 |
|
|
318
|
+
| Class | Under 200 lines, max 300 |
|
|
319
|
+
| Source file | Under 300 lines, max 500 |
|
|
320
|
+
| Method parameters | Max 5; use a record or builder for more |
|
|
321
|
+
|
|
322
|
+
### Early Returns (Guard Clauses)
|
|
323
|
+
|
|
324
|
+
```java
|
|
325
|
+
// GOOD: Guard clauses
|
|
326
|
+
public Post publish(Post post, User user) {
|
|
327
|
+
if (!post.getAuthorId().equals(user.getId())) {
|
|
328
|
+
throw new ForbiddenException("Cannot publish another user's post");
|
|
329
|
+
}
|
|
330
|
+
if (post.getStatus() == PostStatus.PUBLISHED) {
|
|
331
|
+
throw new ConflictException("Post is already published");
|
|
332
|
+
}
|
|
333
|
+
if (post.getBody() == null || post.getBody().isBlank()) {
|
|
334
|
+
throw new ValidationException("Post body is required");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
post.setStatus(PostStatus.PUBLISHED);
|
|
338
|
+
post.setPublishedAt(Instant.now());
|
|
339
|
+
return postRepository.save(post);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// BAD: Deeply nested
|
|
343
|
+
public Post publish(Post post, User user) {
|
|
344
|
+
if (post.getAuthorId().equals(user.getId())) {
|
|
345
|
+
if (post.getStatus() != PostStatus.PUBLISHED) {
|
|
346
|
+
if (post.getBody() != null && !post.getBody().isBlank()) {
|
|
347
|
+
post.setStatus(PostStatus.PUBLISHED);
|
|
348
|
+
return postRepository.save(post);
|
|
349
|
+
} else { throw new ValidationException("..."); }
|
|
350
|
+
} else { throw new ConflictException("..."); }
|
|
351
|
+
} else { throw new ForbiddenException("..."); }
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Import Organization
|
|
358
|
+
|
|
359
|
+
```java
|
|
360
|
+
// 1. java.* (standard library)
|
|
361
|
+
import java.time.Instant;
|
|
362
|
+
import java.util.List;
|
|
363
|
+
import java.util.Optional;
|
|
364
|
+
|
|
365
|
+
// 2. javax.*/jakarta.* (extensions)
|
|
366
|
+
import jakarta.validation.constraints.NotBlank;
|
|
367
|
+
|
|
368
|
+
// 3. Third-party libraries (alphabetical by group)
|
|
369
|
+
import org.springframework.stereotype.Service;
|
|
370
|
+
import org.springframework.transaction.annotation.Transactional;
|
|
371
|
+
|
|
372
|
+
// 4. Project imports
|
|
373
|
+
import com.example.user.domain.User;
|
|
374
|
+
import com.example.user.dto.CreateUserRequest;
|
|
375
|
+
|
|
376
|
+
// Rules:
|
|
377
|
+
// - Never use wildcard imports (import java.util.*)
|
|
378
|
+
// - Group with blank lines between sections
|
|
379
|
+
// - Let IDE organize automatically (google-java-format handles this)
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## Anti-Patterns
|
|
385
|
+
|
|
386
|
+
| Anti-Pattern | Problem | Correct Approach |
|
|
387
|
+
|---|---|---|
|
|
388
|
+
| Wildcard imports | Namespace pollution, hidden dependencies | Import specific classes |
|
|
389
|
+
| Mutable DTOs | Thread-safety issues, unexpected mutation | Use records |
|
|
390
|
+
| Deep inheritance hierarchies | Tight coupling, fragile base class | Composition + interfaces |
|
|
391
|
+
| Raw types (`List` instead of `List<User>`) | Type safety bypassed | Always use generics |
|
|
392
|
+
| `Optional.get()` without check | `NoSuchElementException` at runtime | `orElseThrow()` or `map()` |
|
|
393
|
+
| Empty catch blocks | Silent failures | Log and rethrow or handle |
|
|
394
|
+
| String concatenation in loops | O(n^2) performance | `StringBuilder` or `String.join()` |
|
|
395
|
+
| Returning `null` from public methods | NullPointerException in callers | Return `Optional` or throw |
|
|
396
|
+
| God classes (1000+ lines) | Unmaintainable, untestable | Extract focused services |
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
_Write modern Java that your future self will thank you for. Records, sealed types, and pattern matching are not optional -- they are the language._
|