12 Common Mistakes Developers Make with Spring Boot — and How to Avoid Them
Spring Boot is a powerful and opinionated framework — but even the best tools can be misused. Over the years, I’ve seen Spring Boot abused in subtle ways that later cause debugging headaches, performance issues, or security flaws.
Here are 12 common mistakes I’ve seen (and sometimes made) when working with Spring Boot — along with how to avoid them.
1. Overusing Annotations
Spring makes things easier with annotations, but overuse or misuse is dangerous.
- Don’t mark utility classes as @Component — If a class is stateless and used for static-like functionality, keep it outside the Spring context.
- Avoid redundant annotations — @RestController already includes @ResponseBody, no need to stack both.
- Prefer constructor injection — It’s cleaner, easier to test, and avoids hidden dependencies.
✅ Be intentional with annotations. Don’t just throw them around to “make it work.”
2. Ignoring Spring Profiles and Property Configuration
Hardcoding values or maintaining a single application.properties file for all environments is a recipe for mistakes.
- Use application-{profile}.yml (e.g., application-dev.yml)
- Activate profiles using: spring.profiles.active=dev
- Externalize secrets — never commit passwords or tokens.
Use profiles to cleanly separate config and avoid surprises in production.
3. Throwing Generic Exceptions
Throwing Exception or RuntimeException doesn’t help your team understand or debug issues.
Bad:
throw new RuntimeException("User not found");Better:
throw new UserNotFoundException("User with ID x not found");Also, handle them properly using @ControllerAdvice:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound(UserNotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
}
}4. Logging: Too Little or Too Much
Logging is a double-edged sword.
Common issues:
- No logs when something goes wrong
- Logging full exception stack traces everywhere
- Logging user PII (personally identifiable information)
- Misusing log levels (DEBUG in production, ERROR for expected validation issues)
✅ Tips:
- Use meaningful log messages
- Respect log levels: INFO for normal ops, DEBUG for dev, ERROR for actual system failures
- Sanitize or omit sensitive data
- Use structured logging if possible
5. Improper Database Management
Spring Data JPA is convenient, but…
- Don’t blindly trust generated queries.
- Index your columns correctly.
- Profile your queries (use tools like Hibernate logs, PostgreSQL EXPLAIN, etc.)
- Use @Query with native SQL when needed for performance.
6. Neglecting Spring Boot Actuator
Spring Boot Actuator exposes valuable endpoints like /health, /metrics, and /env.
✅ Integrate it early:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>Also, configure which endpoints are exposed and secure them.
7. Loading Too Many Beans
Letting Spring auto-scan your entire classpath is a silent performance killer.
✅ Use scoped scanning:
@ComponentScan(basePackages = {"com.myproject.service"})8. Using Field Injection Over Constructor Injection
Field injection is not test-friendly and hides required dependencies.
✅ Prefer this:
@RequiredArgsConstructor
@Service
public class OrderService {
private final PaymentService paymentService;
}9. Monolithic Design in Microservice Disguise
Just because you’re deploying multiple Spring Boot apps doesn’t mean you’re doing microservices.
- Shared database? ❌
- Tight coupling? ❌
✅ Use APIs, event-driven design (Kafka, RabbitMQ), and clear service boundaries.
10. Scattered Try-Catch Blocks
Don’t put try/catch around every controller or service method. Centralize exception handling using @ControllerAdvice.
It improves readability, consistency, and maintenance.
11. Mixing Business Logic Inside Controllers
Controllers should handle web concerns — not contain business logic. Push business rules to service layers.
✅ Clean code means better tests and fewer bugs.
12. Poor Class Access and Project Structure
This is a silent killer in growing Spring Boot projects. If everything is public, in one giant package (com.example.project), with no clear domain boundaries — your code becomes hard to navigate and test.
Common mistakes:
- All classes in one package (e.g., controller, service, utils)
- No separation by feature or domain
- Overexposed classes (public everything)
✅ Suggestions:
- Use feature-based package structure (e.g., user, order, inventory) instead of just layers.
- Respect encapsulation — not everything needs to be public.
- Follow Domain-Driven Design (DDD) or Clean Architecture to separate:
- API layer (controllers)
- Business logic (services/domain)
- Persistence (repositories)
- Utilities (common shared code)
com.example.app
├── user
│ ├── UserController
│ ├── UserService
│ ├── UserRepository
│ └── dto
├── order
│ ├── OrderController
│ ├── OrderService
│ ├── OrderRepositoryThis structure promotes modularity, makes testing easier, and helps onboard new developers faster.
✅ Final Wrap-Up
You now have a practical checklist of 12 Spring Boot anti-patterns. Avoiding these can dramatically improve the quality, scalability, and maintainability of your applications.
If you’ve seen other mistakes — or learned a hard lesson — I’d love to hear it. Drop a comment or share the post!
