Skip to main content

☕ Backend Development

"A well-designed backend is the foundation of any scalable application."

Master the Java ecosystem for building robust, high-performance backend services.


🍃 Spring Boot

Core Concepts

Application Startup Flow

Essential Annotations

AnnotationPurpose
@SpringBootApplicationEntry point (combines 3 annotations)
@RestControllerREST API endpoints
@ServiceBusiness logic beans
@RepositoryData access beans
@ConfigurationJava-based configuration
@BeanDefine Spring-managed beans
@AutowiredDependency injection

Auto-Configuration

// Spring Boot auto-configures based on classpath
// Add starter → Get configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.url")
@AutoConfiguration
public class DataSourceAutoConfiguration {
// Automatically creates DataSource bean
}

Request Handling

@RestController
@RequestMapping("/api/v1/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@Valid @RequestBody CreateUserRequest request) {
return userService.create(request);
}

@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(UserNotFoundException ex) {
return new ErrorResponse(ex.getMessage());
}
}

⚡ Concurrency (JUC)

Thread Pools

// ✅ Recommended: Use thread pools, not raw threads
ExecutorService executor = Executors.newFixedThreadPool(10);

// Better: CustomThreadPoolExecutor with tuned parameters
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // corePoolSize
10, // maxPoolSize
60L, TimeUnit.SECONDS, // keepAliveTime
new LinkedBlockingQueue<>(100), // workQueue
new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);

CompletableFuture

// Async composition
public CompletableFuture<OrderSummary> processOrder(Order order) {
return CompletableFuture
.supplyAsync(() -> validateOrder(order))
.thenCompose(this::checkInventory)
.thenCombine(calculateShipping(order), this::createSummary)
.exceptionally(ex -> handleError(ex));
}

// Run multiple tasks in parallel
CompletableFuture.allOf(task1, task2, task3)
.thenAccept(v -> {
// All tasks completed
});

Locks and Synchronization

MechanismUse Case
synchronizedSimple mutual exclusion
ReentrantLockMore control (tryLock, fair)
ReadWriteLockMultiple readers, single writer
StampedLockOptimistic read locking
SemaphoreLimit concurrent access
// ReentrantLock with timeout
private final ReentrantLock lock = new ReentrantLock();

public void updateResource() {
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// Critical section
} finally {
lock.unlock();
}
} else {
throw new TimeoutException("Could not acquire lock");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

🧠 JVM Internals

Memory Model

Garbage Collection

GC TypeCharacteristicsBest For
G1Low latency, balancedGeneral purpose (default)
ZGCUltra-low pause (under 10ms)Large heaps, latency-critical
ParallelHigh throughputBatch processing
ShenandoahLow pause, concurrentRedHat/OpenJDK

JVM Tuning Parameters

# Common production settings
java -Xms4g -Xmx4g \ # Heap size (min = max)
-XX:+UseG1GC \ # Use G1 garbage collector
-XX:MaxGCPauseMillis=200 \ # Target pause time
-XX:+HeapDumpOnOutOfMemoryError \
-Xlog:gc*:file=gc.log \ # GC logging
-jar app.jar

Profiling & Monitoring

# JFR (Java Flight Recorder)
jcmd <pid> JFR.start duration=60s filename=recording.jfr

# jstat - GC statistics
jstat -gcutil <pid> 1000

# jmap - Heap dump
jmap -dump:live,format=b,file=heap.hprof <pid>

📐 API Design

REST Best Practices

HTTP MethodPurposeIdempotent
GETRetrieve resourceYes
POSTCreate resourceNo
PUTReplace resourceYes
PATCHPartial updateYes
DELETERemove resourceYes

Response Structure

// Consistent API response
public record ApiResponse<T>(
boolean success,
T data,
String message,
Map<String, Object> metadata
) {
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(true, data, null, null);
}

public static <T> ApiResponse<T> error(String message) {
return new ApiResponse<>(false, null, message, null);
}
}

📝 Detailed Topics


Performance Tips
  1. Connection pooling - Use HikariCP (Spring Boot default)
  2. Lazy loading - Only fetch data when needed
  3. Batch operations - Reduce database round trips
  4. Async when possible - Don't block for I/O
  5. Profile first - Measure before optimizing