Java Atlas for Full-Stack Engineers
Fundamentals
Primitive Types vs Wrapper Types
Primitive types store raw values, while wrapper types are objects that can be null and are required by generics/collections.
1 | int age = 18; |
== vs equals()
== compares primitive values or object references, while equals() compares object content when the class implements it accordingly.
1 | String a = new String("hello"); |
hashCode()
hashCode() returns a hash value used by hash-based collections such as HashMap; objects that are equal according to equals() must return the same hash code.
1 | String a = "hello"; |
String Immutability
Java String objects cannot be changed after creation; operations create a new string instead.
1 | String a = "hello"; |
Exception Model
Java has checked exceptions, which must be caught or declared with throws, and unchecked exceptions, which may fail at runtime without explicit handling.
Checked exceptions represent expected external failures and must be handled or declared; unchecked exceptions usually indicate programming errors and may fail at runtime.
1 | void readFile() throws IOException { |
Generics
Generics let classes and methods work with different types while keeping compile-time type safety.
1 | List<String> names = new ArrayList<>(); |
Optional
Optional<T> represents a value that may or may not exist, instead of directly using null.
1 | Optional<String> name = Optional.of("Alice"); |
Annotations
Annotations add metadata to classes, methods, or fields, and are heavily used by frameworks such as Spring to configure behavior.
1 |
|
record
A record is a concise way to define an immutable data carrier; Java automatically generates the constructor, accessors, equals(), hashCode(), and toString().
1 | record User(String name, int age) {} |
List, Set, Map
List stores ordered values, Set stores unique values, and Map stores key-value pairs.
1 | List<String> names = List.of("Alice", "Bob"); |
ArrayList
ArrayList is the most common List implementation and is backed by a dynamically resizable array.
1 | List<String> names = new ArrayList<>(); |
Compared with List:
Listis the interface.ArrayListis a concrete implementation.
HashMap
HashMap is the most common Map implementation for key-value storage and is not thread-safe.
1 | Map<String, Integer> ages = new HashMap<>(); |
Compared with Map:
Mapis the interface.HashMapis a concrete implementation.
ConcurrentHashMap
ConcurrentHashMap is a thread-safe Map implementation designed for concurrent access.
1 | Map<String, Integer> ages = new ConcurrentHashMap<>(); |
Compared with HashMap:
HashMapis not thread-safe.ConcurrentHashMapsafely supports concurrent access.- Compound operations can still have race conditions.
Stream API
The Stream API provides a functional, chainable way to transform and process collections, similar to JavaScript’s filter, map, and reduce.
1 | List<String> names = users.stream() |
Lambda Expressions
Java lambdas are anonymous functions, similar to JavaScript arrow functions.
1 | // Java |
1 | // JavaScript |
Functional Interfaces
A functional interface gives a lambda a concrete Java type. It has exactly one abstract method, and the lambda provides that method’s implementation.
1 |
|
In practice, Java often uses built-in functional interfaces such as Function, Predicate, and Consumer.
Method References
Method references are shorthand for lambdas that only call an existing method.
1 | users.stream() |
User::isActive is equivalent to user -> user.isActive().
Java Runtime and Build System
JDK, JRE, and JVM
JDK provides Java development tools, JRE provides the runtime environment, and JVM executes compiled Java bytecode.
1 | .java → javac → .class bytecode → JVM |
JDK = JRE + development tools, while JRE = JVM + runtime libraries.
Java Bytecode and javac
javac compiles Java source code into platform-independent bytecode, which is stored in .class files and executed by the JVM.
1 | Hello.java |
Java Build and Runtime Flow
Java source code is compiled into bytecode, then executed by the JVM.
1 | Main.java |
Example source:
1 | package com.example; |
The package normally matches the directory:
1 | src/main/java/com/example/Main.java |
After compilation:
1 | target/classes/com/example/Main.class |
A .class file contains the compiled bytecode for one Java class.
To run it:
1 | java -cp target/classes com.example.Main |
Here:
1 | -cp target/classes |
sets the classpath, which tells Java where to look for compiled classes.
Java resolves:
1 | com.example.Main |
A JAR packages many .class files and resources into one distributable file.
1 | .java source |
Example:
1 | app.jar |
A normal JAR can be used as a classpath entry:
1 | java -cp app.jar com.example.Main |
Spring Boot usually creates an executable JAR that can run directly:
1 | java -jar app.jar |
In a Maven project, Maven usually handles compilation, classpath, dependencies, and packaging for you:
1 | ./mvnw package |
Maven
Maven is a build and dependency management tool for Java projects. It handles tasks such as compilation, testing, packaging, and running plugins.
1 | ./mvnw compile |
pom.xml
pom.xml is Maven’s project configuration file. It defines project metadata, dependencies, Java version, plugins, and build settings.
1 | <dependency> |
Dependency Management
Maven downloads declared dependencies and their transitive dependencies from repositories, then adds them to the project’s classpath automatically.
1 | pom.xml |
Maven Wrapper
mvnw is a project-local wrapper around Maven that downloads and uses the Maven version configured by the project.
1 | ./mvnw package |
This avoids requiring every developer to install the same Maven version globally.
Spring Boot
Spring Boot Application Structure
A typical Spring Boot application is organized into layers such as controller, service, and repository.
1 | src/main/java/com/example/app/ |
Application.java: application entry point.controller: handles HTTP requests and responses.service: contains business logic.repository: handles database access.entity: represents persistent database data.dto: defines API request/response data shapes.config: contains application and framework configuration.
Typical request flow:
1 | HTTP Request |
DTO, Entity, and Repository have different responsibilities:
1 | DTO = API data shape |
Application Startup
A Spring Boot application starts from a standard Java main() method and boots the Spring application context.
1 |
|
1 | main() |
IoC Container
The Spring IoC Container creates and manages application objects (beans) instead of requiring the application to instantiate them manually.
1 | Spring IoC Container |
Dependency Injection
Dependency Injection means Spring provides an object’s dependencies instead of the object creating them itself.
1 | public UserController(UserService userService) { |
Instead of:
1 | UserService userService = new UserService(); |
Spring Beans
A Spring Bean is a Java object created, configured, and managed by the Spring IoC Container.
1 |
|
UserService becomes a Spring-managed bean.
Component Scanning
Spring scans configured packages for annotated classes and automatically registers them as beans.
1 | com.example.app |
Classes under the application’s base package are typically scanned automatically.
@Component
@Component marks a general-purpose class as a Spring-managed bean.
1 |
|
@Service
@Service is a specialized @Component used for business logic.
1 |
|
@Repository
@Repository is a specialized @Component used for data access.
1 |
|
@Configuration
@Configuration marks a class that defines Spring configuration and beans.
1 |
|
application.yml
application.yml stores application configuration such as ports, database settings, and environment-specific values.
1 | server: |
Profiles
Profiles allow different configurations for environments such as development, testing, and production.
1 | spring: |
Common profiles:
1 | dev |
Starters
Spring Boot starters are dependency bundles for common features.
1 | <dependency> |
For example, spring-boot-starter-web brings in the dependencies needed for building web applications.
Auto-Configuration
Spring Boot automatically configures common components based on the dependencies and configuration found in the project.
1 | spring-boot-starter-web |
This reduces the amount of manual framework configuration required.
Spring Web / MVC
- Controllers: receive HTTP requests and return responses.
- Request Mapping: maps HTTP methods and paths to controller methods.
- Path Variables: read dynamic values from the URL path, e.g.
/users/{id}. - Query Parameters: read values from the query string, e.g.
/users?role=admin. - Request Bodies: convert request JSON into Java objects with
@RequestBody. - DTOs: define API request/response data shapes.
- Validation: validate incoming DTOs with annotations such as
@NotBlank,@Email, and@Valid. - Response Handling: return objects directly or use
ResponseEntityfor explicit status/header control. - Global Exception Handling: handle controller exceptions centrally with
@RestControllerAdvice. - Jackson: converts JSON ↔ Java objects automatically.
- Filters: run around requests at the servlet level, before Spring MVC controller handling.
- Interceptors: run inside Spring MVC before/after controller execution.
- Request Lifecycle:
1 | HTTP Request |
Typical controller:
1 |
|
JPA and Hibernate
ORM Basics
ORM (Object-Relational Mapping) maps Java objects to relational database tables.
1 | Java Object |
Example:
1 |
|
This User entity can be mapped to a database table such as users.
JPA vs Hibernate
JPA stands for Java Persistence API. It is a standard specification for ORM in Java.
Hibernate is one of the most common JPA implementations.
1 | Your Code |
A useful mental model:
1 | JPA = specification / interface |
Entities
An entity is a Java class that represents persistent data stored in the database.
1 |
|
The entity defines the persistence model, while actual data exists as Java objects and database rows.
1 | User user = new User(); |
Primary Keys
@Id marks the primary key of an entity.
1 |
|
@GeneratedValue tells JPA that the ID should usually be generated automatically.
Repositories
Repositories provide the data access layer for entities.
1 | public interface UserRepository |
The generic types mean:
1 | User = entity type |
You can then use:
1 | userRepository.save(user); |
Spring Data JPA
Spring Data JPA is a Spring abstraction built on top of JPA.
It automatically provides common CRUD and query operations, so you do not need to write most repository boilerplate manually.
1 | Application Code |
Entity Relationships
JPA can map relationships between entities and database tables.
Common relationship types:
1 | @OneToOne |
One-to-One
One entity is associated with exactly one other entity.
1 |
|
Example:
1 | User 1 ─── 1 Profile |
One-to-Many
One entity is associated with many child entities.
1 |
|
Example:
1 | User 1 ─── N Task |
Many-to-One
Many entities reference the same parent entity.
1 |
|
Example:
1 | Task N ─── 1 User |
One-to-Many and Many-to-One are often two sides of the same database relationship.
Lazy Loading
Lazy loading delays loading related data until it is actually accessed.
1 | User user = userRepository.findById(1L).orElseThrow(); |
Hibernate may initially load only the user:
1 | SELECT * FROM users WHERE id = 1; |
Then when:
1 | user.getTasks(); |
is accessed, Hibernate may issue another query:
1 | SELECT * FROM tasks WHERE user_id = 1; |
Lazy loading avoids loading unnecessary related data, but can cause extra queries.
Eager Loading
Eager loading loads related data immediately with the parent entity.
1 | load User |
This can be convenient, but may load more data than needed.
N+1 Query Problem
The N+1 problem happens when one query loads a list of entities, then one additional query is executed for each entity’s related data.
Example:
1 | List<User> users = userRepository.findAll(); |
Possible query pattern:
1 | 1 query → load all users |
For 100 users:
1 | 1 + 100 = 101 queries |
This can cause serious performance problems.
Persistence Context
The persistence context is Hibernate’s managed environment for entity objects during a session or transaction.
When an entity is loaded:
1 | User user = userRepository.findById(1L).orElseThrow(); |
Hibernate keeps track of that object.
Conceptually:
1 | Database Row |
The same entity loaded again within the same context may reuse the already managed object instead of creating another independent copy.
Dirty Checking
Dirty checking means Hibernate automatically detects changes to managed entities.
1 |
|
Even without calling:
1 | userRepository.save(user); |
Hibernate can detect that the managed entity changed and generate an update when the transaction is committed.
1 | UPDATE users |
Typical persistence flow:
1 | Controller |
And when reading data:
1 | Database Row |
Transactions
Transaction Basics
A transaction groups multiple database operations into one unit of work.
1 | all succeed |
Example:
1 | create order |
These operations should usually succeed or fail together.
@Transactional
@Transactional tells Spring to execute a method inside a database transaction.
1 |
|
Spring typically opens a transaction before the method runs and commits it when the method finishes successfully.
Commit and Rollback
A successful transaction is committed to the database.
1 | method succeeds |
If the transaction fails, changes can be rolled back.
1 | method throws exception |
Transaction Boundaries
The transaction boundary defines where a transaction starts and ends.
1 |
|
Transactions are commonly placed at the service layer because one business operation may involve multiple repository calls.
Transaction Propagation
Propagation defines what happens when one transactional method calls another transactional method.
The default behavior is usually:
1 | existing transaction |
This is Propagation.REQUIRED.
1 |
|
Both operations can participate in the same transaction.
Rollback Rules
By default, Spring normally rolls back for unchecked exceptions such as RuntimeException.
1 |
|
The saved order is rolled back.
Checked exceptions may require explicit configuration:
1 |
Common @Transactional Pitfalls
A common pitfall is calling a transactional method from another method in the same class.
1 | public void create() { |
Spring transactions are usually implemented with proxies, so self-invocation can bypass the transactional proxy.
Another common mistake is catching an exception and hiding it:
1 |
|
Spring may think the method succeeded and commit the transaction.
Database Connections and Transactions
A database transaction normally operates through a database connection.
Conceptually:
1 | Spring |
In Spring Boot, connections are commonly obtained from a connection pool such as HikariCP.
Typical flow:
1 | Controller |
The service method usually defines the business transaction boundary.
Spring Security Basics
Authentication
Authentication verifies who the user is.
Example:
1 | email + password |
Authorization
Authorization decides what an authenticated user is allowed to do.
1 | USER → read profile |
Security Filter Chain
Spring Security processes requests through a chain of security filters before they reach controllers.
1 | HTTP Request |
SecurityContext
SecurityContext stores information about the currently authenticated user during the request.
1 | SecurityContext |
Password Hashing
Passwords should not be stored directly. Spring commonly uses PasswordEncoder, such as BCrypt, to store password hashes.
1 | passwordEncoder.encode("password"); |
1 | password |
JWT Authentication
JWT is commonly used for stateless API authentication.
1 | Login |
Usually the token is sent as:
1 | Authorization: Bearer <token> |
Method-Level Security
Authorization rules can also be applied directly to service or controller methods.
1 |
|
CORS
CORS controls whether a browser frontend from another origin is allowed to call the API.
1 | frontend: http://localhost:3000 |
Spring must allow the frontend origin when cross-origin requests are needed.
CSRF
CSRF is an attack where a browser is tricked into sending an authenticated request without the user’s intention.
It mainly matters when authentication relies on automatically sent credentials such as cookies.
For stateless APIs using JWTs in the Authorization header, CSRF protection is often configured differently or disabled depending on the architecture.
Typical Flow
1 | HTTP Request |
Concurrency Basics
Threads
A thread is an independent execution path inside a process. A Spring Boot application can handle multiple requests concurrently using multiple threads.
1 | Request A → Thread 1 |
Thread Safety
Code is thread-safe when multiple threads can use it concurrently without corrupting shared state or producing incorrect results.
Race Conditions
A race condition happens when multiple threads read and modify the same shared state at the same time.
1 | class Counter { |
Two threads may both read the same old value:
1 | Thread A reads 0 |
The expected result is 2, but the actual result may be 1.
synchronized
synchronized allows only one thread at a time to execute a protected method or block for the same lock.
1 | class Counter { |
This prevents multiple threads from modifying the protected state at the same time.
Atomic Types
For simple atomic operations such as counters, Java provides classes such as AtomicInteger.
1 | AtomicInteger count = new AtomicInteger(0); |
This is often simpler than manually using synchronized.
volatile
volatile guarantees visibility: when one thread updates a variable, other threads can see the latest value.
1 | class Worker { |
volatile does not make compound operations atomic.
1 | private volatile int count = 0; |
1 | volatile → visibility |
Thread Pools
Creating a new thread for every task is expensive. A thread pool keeps reusable worker threads and assigns tasks to them.
1 | Tasks |
ExecutorService
ExecutorService is Java’s high-level API for submitting work to a thread pool.
1 | ExecutorService executor = |
Instead of manually creating:
1 | new Thread(() -> doWork()).start(); |
the executor manages and reuses worker threads.
CompletableFuture
CompletableFuture represents an asynchronous result and is conceptually similar to a JavaScript Promise.
1 | CompletableFuture |
Rough JavaScript equivalent:
1 | fetchUser() |
Common mappings:
1 | JavaScript Promise Java CompletableFuture |
Java asynchronous work is commonly executed using threads or thread pools, while JavaScript typically uses an event-loop-based model.
Shared Mutable State
Shared mutable state is data that:
- can be changed, and
- can be accessed by multiple threads.
1 | private int count = 0; |
Shared mutable state is one of the main sources of concurrency bugs.
Prefer immutable data or method-local variables when possible.
Singleton Bean Thread Safety
Spring beans are singleton by default, so multiple HTTP requests may use the same bean instance concurrently.
1 |
|
Conceptually:
1 | Request A ─┐ |
A better default is to keep Spring services stateless:
1 |
|
If shared mutable state is actually required, use the appropriate synchronization mechanism or move the state to a system designed for shared data, such as a database or Redis.
JVM Basics
JVM Memory Model Overview
At runtime, the JVM manages several memory areas.
1 | JVM Memory |
Stack vs Heap
Each thread has its own stack for method calls and local variables. The heap stores objects shared across the application.
1 | User user = new User("Alice"); |
1 | Stack |
Object Allocation
Objects created with new are generally allocated on the heap.
1 | User user = new User(); |
The local variable stores a reference to the object rather than the object itself.
Garbage Collection
Garbage Collection automatically reclaims heap memory from objects that are no longer reachable.
1 | User user = new User(); |
The old User object becomes eligible for garbage collection once nothing reachable refers to it.
Class Loading
Compiled .class files must be loaded into the JVM before they can be used.
1 | .java |
Class loaders locate classes from classpath entries such as directories and JAR files.
JVM Memory Settings
Heap size can be configured when starting the JVM.
1 | java -Xms512m -Xmx2g -jar app.jar |
1 | -Xms → initial heap size |
Thread Dumps
A thread dump is a snapshot of what all JVM threads are doing at a specific moment.
It is useful for diagnosing:
1 | deadlocks |
Heap Dumps
A heap dump is a snapshot file containing the objects currently stored in JVM heap memory and their references.
1 | heapdump.hprof |
It is useful for diagnosing:
1 | memory leaks |
A heap dump can be generated with JVM tools such as jcmd:
1 | jcmd <pid> GC.heap_dump heapdump.hprof |
The JVM can also generate one automatically on OOM:
1 | java \ |
Caching & Redis
Why Caching
Caching stores frequently accessed data in faster storage to reduce latency and database load.
1 | Client |
Typical flow:
1 | cache hit → return cached data |
Redis Basics
Redis is an in-memory data store commonly used for caching, sessions, counters, queues, rate limiting, and distributed coordination.
1 | Application |
In distributed deployments, Redis usually runs as a shared service that multiple application instances access over the network.
1 | Machine A / App A ─┐ |
This shared access allows different machines and processes to coordinate through the same Redis state.
Common Data Types
Common Redis data structures include:
1 | String → simple values / counters |
Cache-Aside Pattern
A common caching pattern is:
1 | read request |
The application controls when data enters and leaves the cache.
TTL (Time To Live) / Expiration
Cached values usually have an expiration time.
1 | user:123 |
After the TTL expires, Redis automatically removes the key.
Cache Invalidation
When database data changes, the corresponding cache may become stale.
Typical approach:
1 | update database |
The database normally remains the source of truth.
Cache Penetration / Stampede / Avalanche
Cache penetration: repeated requests for data that does not exist reach the database every time.
1 | Redis miss |
Cache stampede: a hot key expires and many requests query the database simultaneously.
1 | hot key expires |
Cache avalanche: many cached keys expire around the same time, causing a large database traffic spike.
Common protections include:
1 | short-term caching of missing values |
Distributed Locks
A distributed lock ensures that only one application instance performs a shared operation at a time.
A local Java lock such as synchronized only works inside one JVM:
1 | Machine A → JVM A → local lock |
It cannot coordinate multiple machines.
Redis can provide a lock that all application instances can see:
1 | Machine A / App A ─┐ |
Conceptually:
1 | SET lock:report instance-A NX EX 30 |
1 | NX → create only if the lock does not already exist |
If App A acquires the lock:
1 | App A → executes task |
Expiration helps prevent the lock from remaining forever if the owner crashes.
Production applications commonly use mature libraries such as Redisson instead of implementing distributed locking manually.
Redis vs Database
Redis is usually not a replacement for the primary database.
1 | Database → durable source of truth |
Messaging & Kafka
Message Queue Basics
Messaging decouples services by allowing work to be processed asynchronously.
1 | Producer |
Instead of:
1 | Service A |
the producer can publish a message and continue.
Kafka Basics
Kafka is a distributed event-streaming platform commonly used for asynchronous communication, event-driven systems, and high-throughput data processing.
1 | Producer |
Producer / Consumer
A producer publishes messages.
A consumer reads and processes messages.
1 | Order Service |
Topic
A topic is a named stream of messages.
1 | orders |
Producers write to topics and consumers subscribe to them.
Partitions
A Kafka topic is split into partitions for scalability and parallel processing.
1 | orders |
Each partition is an ordered log.
Kafka guarantees ordering within a partition, not across the entire topic.
When a message has a key, the producer typically uses that key to determine the partition.
Conceptually:
1 | partition = hash(key) % numberOfPartitions |
For example, if orderId is used as the key:
1 | OrderCreated key=1001 |
Messages with the same key normally go to the same partition, which preserves ordering for that key.
Without a key, messages can be distributed across partitions for better load balancing and throughput.
1 | same key → same partition → ordering |
Consumer Groups
Consumers in the same consumer group share the work.
1 | Topic |
Within one consumer group, a partition is assigned to only one consumer at a time.
Different consumer groups can independently consume the same topic.
Offset
An offset represents a message’s position inside a partition.
1 | offset 0 → message A |
Consumers track offsets to know how far they have processed.
1 | processed through offset 2 |
At-Least-Once Delivery
A common Kafka delivery model is at least once, meaning a message may occasionally be processed more than once.
For example:
1 | consumer processes message |
Therefore consumers should often be idempotent.
Idempotency
An operation is idempotent if processing the same event multiple times does not create incorrect duplicate effects.
1 | OrderCreated #123 |
Retry / Dead Letter Handling
Failed messages may be retried.
1 | message |
A dead-letter topic stores repeatedly failing messages for later inspection or reprocessing.
Kafka vs REST
REST is synchronous and request/response oriented:
1 | Service A |
Kafka is asynchronous and event oriented:
1 | Service A |
Use REST when an immediate response is needed.
Use Kafka when asynchronous processing, loose coupling, event distribution, or high throughput is useful.
Production Infrastructure
Load Balancer
A load balancer distributes incoming traffic across multiple application instances.
1 | Clients |
This enables horizontal scaling and improves availability.
API Gateway
An API gateway provides a centralized entry point for backend APIs.
1 | Client |
Common responsibilities include:
1 | routing |
A load balancer mainly distributes traffic, while an API gateway applies API-level routing and policies.
Nginx
Nginx is a high-performance web server and reverse proxy commonly placed in front of backend applications.
1 | Client |
Common uses include:
1 | reverse proxy |
Reverse Proxy
A reverse proxy receives client requests and forwards them to internal backend services.
1 | Client |
Example:
1 | server { |
The client only communicates with Nginx and does not need to know the backend server’s internal address.
Static Files
Nginx can also serve frontend static assets.
1 | Browser |
Load Balancing
Nginx can distribute requests across multiple backend instances.
1 | Client |
TLS Termination
Nginx can handle HTTPS while backend services communicate through an internal network.
1 | Client |
Docker
Docker packages an application and its runtime dependencies into an isolated container so it can run consistently across environments.
1 | Spring Boot Application |
Example:
1 | FROM eclipse-temurin:21-jre |
Build and run:
1 | docker build -t my-app . |
Dockerfile
A Dockerfile contains instructions for building a Docker image.
1 | Dockerfile |
Image vs Container
A Docker image is an immutable application package.
A container is a running instance of an image.
1 | Docker Image |
The same image can run as multiple containers.
1 | my-app image |
Registry
A container registry stores and distributes Docker images.
1 | Developer / CI |
Examples include Docker Hub, Amazon ECR, and Google Artifact Registry.
Docker Compose
Docker Compose allows multiple containers to be defined and started together.
1 | services: |
1 | Docker Compose |
This is commonly useful for local development.
Kubernetes
Kubernetes (K8s) orchestrates containers across multiple machines.
Docker runs containers, while Kubernetes manages containers at scale.
1 | Docker |
A Kubernetes cluster contains multiple machines called nodes.
1 | Kubernetes Cluster |
Pod
A Pod is the smallest deployable unit in Kubernetes.
A Pod commonly contains one application container.
1 | Pod |
Multiple Pods can run replicas of the same application.
1 | Spring Boot |
Deployment
A Deployment manages application Pods and their desired replica count.
1 | apiVersion: apps/v1 |
Kubernetes continuously tries to maintain the desired state.
1 | desired replicas = 3 |
Service
Pods can be created and destroyed, so their IP addresses are not stable.
A Kubernetes Service provides a stable network endpoint for a group of Pods.
1 | Client |
The Service can also distribute traffic between Pods.
ConfigMap and Secret
Configuration should normally be separated from the Docker image.
1 | ConfigMap → normal configuration |
This allows the same Docker image to run with different environment configurations.
Health Checks
Kubernetes can monitor application health.
1 | Liveness Probe |
Spring Boot Actuator endpoints are commonly used for health checks.
Scaling
Kubernetes can increase or decrease the number of application replicas.
1 | traffic increases |
This is horizontal scaling.
Rate Limiting
Rate limiting restricts how frequently a client can call an API.
1 | 100 requests / minute / user |
It helps protect services from abuse and traffic spikes.
Common strategies include:
1 | fixed window |
Redis is commonly used to store shared rate-limit state across multiple application instances.
Background Jobs
Long-running or non-urgent work can be moved out of the HTTP request path.
1 | HTTP Request |
Common examples include:
1 | sending email |
Background jobs can be implemented using job queues, message brokers, or scheduled workers.
Timeout and Retry
External calls should have a maximum waiting time.
1 | Service A |
Without timeouts, slow dependencies can consume application threads and resources indefinitely.
Temporary failures can sometimes be retried.
1 | request |
Retries should normally have a limit and use backoff.
1 | retry 1 → 100ms |
This is commonly called exponential backoff.
Uncontrolled retries can make an outage worse by creating additional traffic.
HTTP Idempotency
Idempotency prevents retries or duplicate requests from creating duplicate side effects.
For example:
1 | POST /payments |
The first request:
1 | abc123 |
If the same request is sent again:
1 | abc123 already processed |
Idempotency is especially important for operations such as:
1 | payments |
Observability
Observability helps understand what a production system is doing.
The three common signals are:
1 | Logs → what happened |
Common metrics include:
1 | request rate |
A distributed trace can show where a request became slow.
1 | API Gateway 10ms |
Typical debugging flow:
1 | slow request |
Typical Production Deployment
A common production architecture may look like:
1 | Internet |
A typical build and deployment flow is:
1 | Java Source |
Key responsibilities:
1 | Nginx |
Java Atlas for Full-Stack Engineers