Java Backend Developer Interview
1. What is Spring Core and why is it important?
Spring Core is the foundational module of the Spring Framework and provides the essential features required to build enterprise Java applications. Its most important responsibility is managing objects and their dependencies using the principles of Inversion of Control (IoC) and Dependency Injection (DI). Instead of developers manually creating and wiring objects, Spring Core delegates this responsibility to the container, improving modularity and testability.
Spring Core promotes loose coupling by allowing developers to define object relationships declaratively using configuration files or annotations. This makes applications easier to maintain, extend, and test. Almost every Spring-based application relies on Spring Core, making it critical to understand before working with Spring MVC, Spring Boot, or Spring Data.
⸻
2. What is Inversion of Control (IoC)?
Inversion of Control (IoC) is a design principle where the control of object creation and lifecycle is transferred from the application code to an external framework or container. In traditional programming, developers explicitly create objects using the new keyword. With IoC, the Spring container is responsible for instantiating, configuring, and managing objects.
In Spring, IoC is achieved through Dependency Injection. This inversion improves flexibility and reduces tight coupling between classes. By relying on interfaces and external configuration, developers can change implementations without modifying the dependent code. IoC also enhances testability because dependencies can be easily mocked or replaced during unit testing.
⸻
3. How does Spring implement IoC?
Spring implements IoC through its IoC container, primarily using the ApplicationContext interface. The container reads configuration metadata (XML, Java configuration, or annotations), creates bean instances, wires dependencies, and manages their lifecycle.
The container first scans configuration files or classes, identifies bean definitions, and instantiates objects. Then it injects dependencies using constructors, setters, or fields. Once the beans are fully initialized, they are made available to the application. The container also manages destruction callbacks when the application shuts down. This centralized object management allows developers to focus on business logic instead of infrastructure concerns.
⸻
4. What is Dependency Injection (DI)?
Dependency Injection is a design pattern used to implement IoC, where an object’s dependencies are provided by an external entity rather than created internally. In Spring, dependencies are injected by the container at runtime.
DI promotes loose coupling, making applications easier to maintain and test. Instead of hardcoding dependencies, classes depend on abstractions such as interfaces. Spring injects the appropriate implementation based on configuration. This approach allows developers to change implementations without modifying the dependent class, improves reusability, and simplifies unit testing by enabling mock dependencies.
⸻
5. What are the types of Dependency Injection in Spring?
Spring supports three types of Dependency Injection: constructor injection, setter injection, and field injection.
Constructor injection is preferred because it ensures that required dependencies are provided at object creation time, making the bean immutable and easier to test. Setter injection is useful for optional dependencies and allows changing values after object creation. Field injection uses annotations directly on fields and is concise, but it is generally discouraged because it makes testing harder and hides dependencies. In real-world projects, constructor injection is considered best practice.
⸻
6. What is the Spring Bean?
A Spring Bean is a Java object that is managed by the Spring IoC container. Beans are defined using configuration metadata such as XML, Java configuration classes, or annotations like @Component.
Spring is responsible for creating, initializing, wiring, and destroying beans. Beans represent the core building blocks of a Spring application. Each bean has a lifecycle that includes instantiation, dependency injection, initialization, and destruction. Understanding how beans are defined and managed is essential for effective Spring development.
⸻
7. What is the Spring IoC Container?
The Spring IoC container is responsible for managing the lifecycle and dependencies of beans. It reads configuration metadata, instantiates beans, injects dependencies, and manages bean scopes.
The two main container types are BeanFactory and ApplicationContext. ApplicationContext is more commonly used because it provides additional features such as event propagation, internationalization, and automatic bean post-processing. The container acts as the backbone of Spring applications, ensuring that objects are loosely coupled and properly configured.
⸻
8. What is the difference between BeanFactory and ApplicationContext?
BeanFactory is the basic container that provides fundamental IoC functionality, while ApplicationContext is an advanced container with enterprise-level features.
BeanFactory uses lazy initialization, meaning beans are created only when requested. ApplicationContext eagerly initializes singleton beans by default, improving startup validation. Additionally, ApplicationContext supports internationalization, application events, and annotation-based configuration. In most modern Spring applications, ApplicationContext is preferred due to its richer feature set.
⸻
9. What are Spring Bean Scopes?
Bean scopes define the lifecycle and visibility of a bean within the Spring container. The scope determines how many instances of a bean are created and how long they live.
Spring provides several scopes such as singleton, prototype, request, session, and application. Choosing the right scope is critical for performance, memory usage, and thread safety. Singleton is the default and most commonly used scope, while web-specific scopes are used in Spring MVC applications.
⸻
10. Explain the Singleton scope.
In the singleton scope, only one instance of a bean is created per Spring container. This instance is shared across the entire application.
Singleton beans are created eagerly by default and are suitable for stateless services. However, they must be thread-safe because the same instance can be accessed by multiple threads simultaneously. Singleton scope improves performance and memory efficiency, making it ideal for service and repository layers.
⸻
11. Explain the Prototype scope.
In prototype scope, a new instance of the bean is created every time it is requested from the container.
Prototype beans are useful for stateful objects where each user or process requires a separate instance. Unlike singleton beans, Spring does not manage the full lifecycle of prototype beans, meaning destruction callbacks are not automatically invoked. Developers must handle cleanup manually. Prototype scope provides flexibility but should be used cautiously due to higher memory consumption.
⸻
12. What are Request and Session scopes?
Request scope creates a new bean instance for each HTTP request, while session scope creates one bean instance per HTTP session.
These scopes are primarily used in web applications. Request-scoped beans are useful for request-specific data, while session-scoped beans are ideal for storing user-specific state. These scopes help manage data lifecycle efficiently in web environments and prevent data leakage across users.
⸻
13. What is Autowiring in Spring?
Autowiring is a feature that allows Spring to automatically resolve and inject bean dependencies without explicit configuration.
Spring supports autowiring by type, by name, and by constructor. Annotation-based autowiring using @Autowired is the most common approach. Autowiring reduces boilerplate code and simplifies configuration, but developers must ensure that bean definitions are unambiguous to avoid runtime errors.
⸻
14. What is the @Autowired annotation?
@Autowired is used to automatically inject dependencies into Spring-managed beans. It can be applied to constructors, setters, or fields.
When Spring encounters @Autowired, it searches the container for a matching bean and injects it. By default, autowiring is mandatory, but it can be made optional. Constructor-based autowiring is preferred because it ensures that all required dependencies are available at object creation time.
⸻
15. What is @Qualifier and why is it needed?
@Qualifier is used to resolve ambiguity when multiple beans of the same type exist in the Spring container.
When Spring finds more than one matching bean for autowiring, it throws an exception. @Qualifier allows developers to specify exactly which bean should be injected. This improves clarity and prevents runtime errors. It is commonly used alongside @Autowired in complex applications where multiple implementations of an interface exist.
⸻
16. Difference between @Primary and @Qualifier?
@Primary marks a bean as the default choice when multiple beans of the same type are available. @Qualifier explicitly specifies which bean to inject.
@Primary is useful when one implementation is used most of the time, while @Qualifier is better when multiple implementations are frequently used. In case both are present, @Qualifier takes precedence. Both annotations help manage dependency ambiguity effectively.
⸻
17. What is Bean Lifecycle in Spring?
The Spring bean lifecycle consists of several phases: instantiation, dependency injection, initialization, and destruction.
During initialization, Spring calls lifecycle callbacks such as @PostConstruct. During destruction, it invokes cleanup methods like @PreDestroy. Developers can customize lifecycle behavior using interfaces or annotations. Understanding the lifecycle is important for resource management, such as opening and closing database connections.
⸻
18. What are @Component, @Service, and @Repository?
These annotations are stereotype annotations used to mark Spring-managed components.
@Component is a generic stereotype, @Service is used for business logic, and @Repository is used for persistence logic. While functionally similar, they improve code readability and enable specialized behaviors like exception translation in @Repository. Using these annotations helps organize application layers clearly.
⸻
19. How does Spring achieve loose coupling?
Spring achieves loose coupling through IoC and DI by allowing classes to depend on interfaces rather than concrete implementations.
Dependencies are injected externally, making it easy to swap implementations without changing the dependent code. This design improves maintainability, testability, and scalability. Loose coupling is one of the key reasons Spring is widely used in enterprise applications.
⸻
20. Why is constructor injection recommended over field injection?
Constructor injection is recommended because it ensures that all required dependencies are available at object creation time.
It makes dependencies explicit, improves immutability, and simplifies unit testing. Field injection hides dependencies and makes testing harder because reflection is required. Constructor injection aligns with clean code principles and is considered best practice in modern Spring applications.
CREATIONAL DESIGN PATTERNS (5)
1. What is the Singleton Design Pattern and when would you use it?
The Singleton design pattern ensures that a class has only one instance throughout the application lifecycle and provides a global access point to that instance. This is particularly useful when exactly one object is required to coordinate actions across the system, such as a configuration manager, logging service, or database connection pool.
In interviews, I emphasize that Singleton is not just about having one instance, but also about controlled access, lazy initialization, and sometimes thread safety. A poorly implemented Singleton can cause issues such as hidden dependencies, difficulty in testing, and concurrency bugs. Therefore, in modern systems, I use it cautiously — often replacing it with dependency injection. Singleton is best suited when the system truly needs a single shared state and when lifecycle control is critical.
2. Explain the Factory Method Pattern with a real-world example.
The Factory Method pattern defines an interface for creating an object but lets subclasses decide which class to instantiate. It helps in decoupling object creation from its usage, allowing the system to be extensible without modifying existing code.
In a real-world example, consider a notification system. A base NotificationFactory might define a method createNotification(). Concrete factories such as EmailNotificationFactory or SMSNotificationFactory decide which notification object to create. This aligns with the Open–Closed Principle, allowing new notification types to be added without changing existing logic.
In interviews, I highlight that Factory Method is ideal when the exact type of object cannot be determined until runtime and when we want to avoid tight coupling with concrete classes.
3. How does the Abstract Factory Pattern differ from Factory Method?
Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes. While Factory Method focuses on creating a single object, Abstract Factory deals with multiple related objects that must work together.
For example, in a cross-platform UI system, an Abstract Factory can create buttons, checkboxes, and windows for Windows or macOS environments. The client code remains unaware of the underlying implementations.
In interviews, I explain that Abstract Factory is useful when consistency among products is required. The downside is increased complexity, especially when new product types are added. However, it is extremely powerful in frameworks and enterprise systems where portability and consistency matter.
4. What is the Builder Pattern and why not just use constructors?
The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It is particularly useful when an object has many optional parameters.
Using constructors for such objects often leads to constructor telescoping, making the code hard to read and maintain. The Builder pattern solves this by providing a step-by-step construction process, often with fluent APIs.
In interviews, I give examples like building a UserProfile or House object with optional fields. Builder improves readability, immutability, and maintainability. It is widely used in modern APIs, especially where object configuration flexibility is required.
5. Explain the Prototype Pattern and its advantages.
The Prototype pattern allows objects to be created by cloning an existing instance rather than creating new ones from scratch. This is especially useful when object creation is expensive in terms of time or resources.
For example, in a game engine, cloning a preconfigured enemy object is much faster than recreating it every time. Prototype helps reduce initialization costs and simplifies object creation logic.
In interviews, I mention that Prototype is useful when classes are loaded dynamically or when the system should avoid subclassing. However, deep cloning can be complex and must be handled carefully to avoid shared references.
STRUCTURAL DESIGN PATTERNS (7)
6. What problem does the Adapter Pattern solve?
The Adapter pattern allows incompatible interfaces to work together by converting one interface into another that the client expects. It acts as a bridge between legacy code and modern systems.
A real-world analogy is a power adapter that allows a device with one plug type to work with a different socket. In software, an Adapter wraps an existing class and exposes a compatible interface.
In interviews, I explain that Adapter is crucial in system integration scenarios. It promotes reusability and avoids rewriting existing code. However, excessive adapters may indicate poor system design.
7. Explain the Bridge Pattern with an example.
The Bridge pattern decouples an abstraction from its implementation so that both can evolve independently. It avoids class explosion caused by inheritance.
For example, consider a RemoteControl abstraction and different Device implementations like TV or Radio. Instead of creating multiple subclasses, Bridge allows both to vary independently.
In interviews, I highlight that Bridge is useful when multiple dimensions of variation exist. It improves maintainability and scalability but introduces additional abstraction layers.
8. What is the Composite Pattern and where is it used?
Composite allows you to treat individual objects and compositions of objects uniformly. It is commonly used to represent tree structures.
Examples include file systems (files and folders) or UI components (buttons inside panels). Clients interact with both leaf and composite objects using the same interface.
In interviews, I stress that Composite simplifies client code and aligns well with recursive structures. The main challenge is enforcing constraints on what children can be added.
9. Explain the Decorator Pattern and how it differs from inheritance.
Decorator allows behavior to be added to objects dynamically without modifying their structure. Unlike inheritance, it provides flexibility by composing objects instead of extending classes.
A common example is adding toppings to a pizza or adding compression and encryption to a data stream. Each decorator wraps another object.
In interviews, I explain that Decorator adheres to the Open–Closed Principle. It avoids class explosion and is widely used in I/O libraries. The downside is increased number of small classes.
10. What is the Facade Pattern and why is it useful?
Facade provides a simplified interface to a complex subsystem. It hides internal complexity and exposes only what the client needs.
For example, a HomeTheaterFacade can manage multiple subsystems like amplifier, projector, and speakers behind a single method call.
In interviews, I highlight that Facade improves usability and reduces coupling. It does not restrict access to subsystem classes but encourages cleaner architecture.
11. Explain the Flyweight Pattern with a practical use case.
Flyweight minimizes memory usage by sharing common object state instead of creating many similar objects.
A classic example is text editors where characters share font and style data. Only the extrinsic state (position) varies.
In interviews, I emphasize that Flyweight is effective in systems with a large number of similar objects. However, it adds complexity and should be used only when memory optimization is critical.
12. What is the Proxy Pattern and its real-world applications?
Proxy provides a placeholder or surrogate to control access to another object. It can be used for lazy loading, security, logging, or caching.
Examples include virtual proxies for large images or remote proxies for network services.
In interviews, I mention that Proxy is useful for performance optimization and access control. However, it can introduce latency and complexity if overused.
BEHAVIORAL DESIGN PATTERNS (11)
13. Explain the Chain of Responsibility Pattern.
Chain of Responsibility passes a request through a chain of handlers until one handles it. This decouples the sender from the receiver.
Examples include logging frameworks or request filters in web applications.
In interviews, I explain that this pattern improves flexibility and aligns with single-responsibility. However, debugging can be harder if the chain is long.
14. What is the Command Pattern and where is it useful?
Command encapsulates a request as an object, allowing parameterization, queuing, and undo operations.
Examples include GUI buttons or transactional systems.
In interviews, I emphasize that Command decouples invokers from receivers and supports undo/redo functionality effectively.
15. Explain the Interpreter Pattern.
Interpreter defines a grammar and interprets sentences in that grammar. It is used for simple languages and expressions.
Examples include SQL parsing or rule engines.
In interviews, I clarify that Interpreter is suitable for small grammars but not for complex languages due to scalability concerns.
16. What is the Iterator Pattern?
Iterator provides a way to access elements of a collection sequentially without exposing its internal structure.
In interviews, I highlight that Iterator supports uniform traversal and enhances encapsulation. It is heavily used in collection frameworks.
17. Explain the Mediator Pattern.
Mediator centralizes communication between objects, reducing direct dependencies.
Examples include chat rooms or UI dialog coordination.
In interviews, I explain that Mediator reduces coupling but can become complex if it handles too many interactions.
18. What is the Memento Pattern and why is it important?
Memento captures an object’s internal state without violating encapsulation, allowing rollback.
Examples include undo functionality in editors.
In interviews, I mention that it is powerful but can be memory-intensive.
19. Explain the Observer Pattern with a real-world example.
Observer defines a one-to-many dependency where observers are notified of state changes.
Examples include event listeners or stock price updates.
In interviews, I emphasize its importance in event-driven systems and note the risk of memory leaks if observers are not managed properly.
20. What is the State Pattern?
State allows an object to alter its behavior when its internal state changes.
Examples include workflow engines or vending machines.
In interviews, I explain that it replaces complex conditionals with polymorphism.
21. Explain the Strategy Pattern.
Strategy defines a family of algorithms and makes them interchangeable.
Examples include payment methods or sorting algorithms.
In interviews, I highlight its flexibility and adherence to Open–Closed Principle.
22. What is the Template Method Pattern?
Template Method defines the skeleton of an algorithm while allowing subclasses to override steps.
Examples include data processing pipelines.
In interviews, I explain that it promotes reuse but relies heavily on inheritance.
23. Explain the Visitor Pattern and its use cases.
Visitor separates operations from object structures, allowing new operations without modifying existing classes.
Examples include AST processing or reporting engines.
In interviews, I mention that Visitor is powerful but violates encapsulation and can be complex to implement.
Design Principles — SOLID Explained with Design Pattern Exercises
SOLID principles guide how we design classes and dependencies, while design patterns provide proven implementation techniques to apply those principles effectively.”
⸻
1. Single Responsibility Principle (SRP)
“A class should have only one reason to change.”
Explanation
SRP means that a class should focus on one responsibility or concern. When a class handles multiple responsibilities — such as business logic, persistence, and logging — it becomes tightly coupled, difficult to test, and fragile to change. Each responsibility should be isolated so that a change in one area does not impact others.
In interviews, I emphasize that SRP is not about having only one method; it’s about cohesion. A class can have many methods as long as they serve one purpose.
⸻
Design Pattern Exercise (SRP + Strategy + Repository)
Problem:
A UserService handles validation, database storage, and email notification.
SRP Violation:
• Validation logic
• Persistence logic
• Notification logic
SRP Solution using Patterns:
• Strategy Pattern → Validation strategies
• Repository Pattern → Persistence
• Observer Pattern → Notifications
Result:
Each class has a single responsibility and can evolve independently.
⸻
2. Open–Closed Principle (OCP)
“Software entities should be open for extension, but closed for modification.”
Explanation
OCP ensures that existing, tested code does not break when new functionality is added. Instead of modifying existing logic, we extend behavior using abstractions and polymorphism.
In interviews, I explain that OCP is impossible without abstractions. Interfaces and abstract classes are the backbone of OCP.
⸻
Design Pattern Exercise (OCP + Strategy Pattern)
Problem:
A payment system uses if-else to handle Credit Card, UPI, and Net Banking.
OCP Violation:
Every new payment method requires modifying the existing class.
OCP Solution:
• Create PaymentStrategy interface
• Implement CreditCardPayment, UPIPayment, etc.
Now, new payment types are added without touching existing code.
⸻
3. Liskov Substitution Principle (LSP)
“Subtypes must be substitutable for their base types.”
Explanation
LSP ensures that derived classes do not alter the expected behavior of base classes. If a subclass breaks functionality, throws unexpected exceptions, or weakens constraints, it violates LSP.
In interviews, I often use the classic Rectangle–Square problem to explain this principle.
⸻
Design Pattern Exercise (LSP + Factory Method)
Problem:
A Bird class has a fly() method. Penguin extends Bird.
LSP Violation:
Penguin cannot fly → breaks substitution.
LSP Solution:
• Create FlyingBird and NonFlyingBird interfaces
• Use Factory Method to return the correct bird type
This ensures all subtypes behave consistently.
⸻
4. Interface Segregation Principle (ISP)
“Clients should not be forced to depend on interfaces they do not use.”
Explanation
ISP promotes small, focused interfaces rather than large, “fat” ones. Large interfaces cause unnecessary dependencies and make implementations harder to maintain.
In interviews, I mention that ISP improves flexibility, testability, and decoupling.
⸻
Design Pattern Exercise (ISP + Adapter Pattern)
Problem:
A Machine interface has print(), scan(), fax().
ISP Violation:
A simple printer must implement unused methods.
ISP Solution:
• Split interfaces: Printable, Scannable, Faxable
• Use Adapter Pattern to adapt legacy machines
Now each class depends only on what it needs.
⸻
5. Dependency Inversion Principle (DIP)
“High-level modules should not depend on low-level modules; both should depend on abstractions.”
Explanation
DIP ensures that business logic is not tightly coupled to implementation details. This makes systems easier to test, extend, and maintain.
In interviews, I explain that DIP is the foundation of Dependency Injection, Hexagonal Architecture, and Clean Architecture.
⸻
Design Pattern Exercise (DIP + Factory + Dependency Injection)
Problem:
OrderService directly creates MySQLDatabase.
DIP Violation:
Business logic depends on concrete infrastructure.
DIP Solution:
• Create Database interface
• Implement MySQLDatabase, PostgreSQLDatabase
• Inject dependency via constructor or factory
This allows easy swapping of implementations without touching business logic.
1. What is Microservices Architecture?
Microservices architecture is an architectural style where an application is built as a collection of small, independent services, each responsible for a specific business capability. Each service runs in its own process, communicates over lightweight protocols (usually HTTP/REST or messaging), and can be developed, deployed, and scaled independently.
In an interview, I explain that microservices are not just about splitting an application but about organizational and operational autonomy. Each service typically owns its own data, follows a bounded context, and aligns well with agile teams. In Java ecosystems, microservices are commonly built using Spring Boot, containerized with Docker, and orchestrated by Kubernetes.
However, microservices introduce complexity such as distributed transactions, network latency, and operational overhead. Therefore, they are best suited for large, evolving systems rather than small applications.
⸻
2. Explain Monolithic vs Microservices Architecture. When would you choose each?
A monolithic architecture packages the entire application — UI, business logic, and data access — into a single deployable unit. It is simple to develop, test, and deploy initially, making it ideal for small teams or early-stage products.
Microservices, in contrast, split the system into independently deployable services. This enables scalability, fault isolation, and faster independent releases. In interviews, I emphasize that microservices solve organizational scaling problems, not just technical ones.
I would choose a monolith when:
• The domain is simple
• Team size is small
• Speed of initial development is critical
I would choose microservices when:
• The system is large and evolving
• Different modules scale differently
• Multiple teams work independently
A common mistake is starting with microservices too early, leading to unnecessary complexity.
⸻
3. What is a Bounded Context and why is it important in Microservices?
A bounded context is a clear boundary within which a domain model is defined and consistent. The same business term can have different meanings in different bounded contexts. For example, “Order” in billing may differ from “Order” in shipping.
In interviews, I explain that bounded context is the foundation of microservice boundaries. Each microservice should ideally map to a single bounded context, owning its data and logic. This avoids tight coupling and reduces cascading changes.
Without bounded contexts, teams often share databases or domain models, which leads to distributed monoliths. Bounded contexts help teams move independently and reduce cognitive load. They are a key concept from Domain-Driven Design (DDD) and are critical for long-term scalability.
⸻
4. What is Domain-Driven Design (DDD) and how does it relate to Microservices?
Domain-Driven Design (DDD) is a software design approach that focuses on modeling software around complex business domains through close collaboration with domain experts. It emphasizes concepts such as entities, value objects, aggregates, repositories, and bounded contexts.
In microservices interviews, I explain that DDD helps define service boundaries correctly. Microservices aligned with DDD are more cohesive and autonomous. Each service represents a business capability rather than a technical layer.
DDD is especially valuable in complex domains like finance or e-commerce. However, it adds modeling overhead and should not be blindly applied to simple CRUD systems. In practice, many teams adopt DDD-lite, focusing mainly on bounded contexts and aggregates.
⸻
5. How does the Single Responsibility Principle apply to Microservices?
The Single Responsibility Principle (SRP) states that a component should have only one reason to change. In microservices, SRP applies at the service level, not just the class level.
In interviews, I explain that a microservice should own one business capability, such as payment processing or order management. If a service handles unrelated responsibilities, it becomes hard to scale, deploy, and reason about.
SRP enables independent deployment and reduces blast radius during failures. Violating SRP leads to services that behave like mini-monoliths. Proper SRP alignment improves maintainability, scalability, and team ownership.
⸻
6. What is Loose Coupling and why is it critical in Microservices?
Loose coupling means that services have minimal knowledge of each other’s internal implementation. They interact through well-defined contracts, typically APIs or events.
In interviews, I emphasize that loose coupling allows services to evolve independently. Changes in one service should not require changes in others. This is achieved through:
• API contracts
• Versioning
• Event-driven communication
Tight coupling in microservices often appears as shared databases or synchronous call chains. Loose coupling improves resilience and enables faster releases, which is one of the primary goals of microservices.
⸻
7. Explain High Cohesion in the context of Microservices.
High cohesion means that all responsibilities within a service are strongly related. A highly cohesive microservice focuses on a single business capability and contains all logic related to it.
In interviews, I explain that high cohesion complements loose coupling. While loose coupling minimizes dependencies between services, high cohesion maximizes clarity within a service.
Highly cohesive services are easier to understand, test, and maintain. Low cohesion leads to “God services” that are difficult to scale and evolve. High cohesion also aligns well with bounded contexts and domain-driven design.
⸻
8. What is Service Autonomy?
Service autonomy means that a microservice can be developed, deployed, scaled, and operated independently of other services. It owns its code, data, and lifecycle.
In interviews, I mention that autonomy is achieved through:
• Database per service
• Independent CI/CD pipelines
• Decentralized governance
Autonomous services reduce coordination overhead between teams and enable faster innovation. However, autonomy must be balanced with organizational standards to avoid chaos. True microservices fail if services cannot evolve independently.
⸻
9. Why should Microservices be Stateless?
Stateless services do not store client session data between requests. Each request contains all the information needed for processing.
In interviews, I explain that statelessness enables:
• Horizontal scaling
• Easier load balancing
• Improved fault tolerance
State is typically stored in external systems such as databases or caches. Stateless services work especially well in cloud-native environments like Kubernetes. Stateful services complicate scaling and recovery, making stateless design a best practice in microservices.
⸻
10. What is Eventual Consistency and why is it acceptable in Microservices?
Eventual consistency means that data across services may be temporarily inconsistent, but will converge to a consistent state over time.
In interviews, I explain that strict consistency is difficult in distributed systems. Microservices favor availability and partition tolerance, making eventual consistency a practical trade-off.
For example, an order service may confirm an order before the inventory service updates stock. This is handled using events and compensating actions. Eventual consistency improves scalability and resilience but requires careful design and user experience considerations.
⸻
11. What is an API Gateway and why is it used in Microservices?
An API Gateway acts as a single entry point for all client requests in a microservices architecture. Instead of clients calling multiple backend services directly, they interact with the API Gateway, which then routes requests to the appropriate services.
In interviews, I explain that the API Gateway handles cross-cutting concerns such as authentication, authorization, rate limiting, request routing, caching, and response aggregation. This simplifies client-side logic and reduces duplication across services.
In Java ecosystems, API Gateways are commonly implemented using Spring Cloud Gateway. While API Gateways improve security and manageability, they can become a bottleneck or single point of failure if not designed for scalability. Therefore, they must be stateless, horizontally scalable, and well-monitored.
⸻
12. What is Service Discovery and why is it required in Microservices?
Service discovery is the mechanism by which microservices dynamically locate each other at runtime. Since services scale up and down and may change IP addresses frequently, hardcoding service locations is impractical.
In interviews, I explain that service discovery solves this by maintaining a registry of available service instances. Services register themselves at startup, and clients query the registry to find service locations. This allows dynamic scaling and resilience.
A common implementation is Netflix Eureka, often integrated with Spring Cloud. Without service discovery, microservices become tightly coupled and difficult to operate in cloud environments.
⸻
13. Explain Synchronous Communication in Microservices (REST / gRPC).
Synchronous communication occurs when a service sends a request and waits for an immediate response. REST over HTTP and gRPC are the most common synchronous communication mechanisms in microservices.
In interviews, I explain that REST is widely used due to its simplicity, statelessness, and interoperability. gRPC, on the other hand, offers high performance, strong typing, and efficient binary serialization using Protocol Buffers.
Synchronous communication is easy to understand and implement, but it introduces tight runtime coupling and increases the risk of cascading failures. Therefore, it must be combined with resilience patterns like timeouts and circuit breakers. It is best suited for request-response scenarios where immediate feedback is required.
⸻
14. What is Asynchronous Messaging and when should it be used?
Asynchronous messaging allows services to communicate without waiting for an immediate response. Messages are sent to a broker and processed independently by consumers.
In interviews, I explain that asynchronous communication improves decoupling, scalability, and fault tolerance. If a consumer service is down, messages can be queued and processed later. This model works well for background processing, notifications, and integrations.
Technologies such as Apache Kafka or RabbitMQ are commonly used. While asynchronous messaging improves resilience, it adds complexity in debugging, monitoring, and handling eventual consistency. Therefore, it should be used where business workflows allow delayed processing.
⸻
15. What is Event-Driven Architecture (EDA)?
Event-Driven Architecture is a design approach where services react to events rather than directly invoking each other. When a state change occurs, a service publishes an event, and interested services consume it.
In interviews, I explain that EDA promotes loose coupling and high scalability. Producers do not know who consumes the event, which allows new services to be added without impacting existing ones.
EDA is commonly used in domains like e-commerce and finance for order processing, inventory updates, and auditing. However, it introduces challenges such as event versioning, schema evolution, and eventual consistency. Proper governance and observability are critical for successful event-driven systems.
⸻
16. Explain the Publish–Subscribe Pattern in Microservices.
The Publish–Subscribe (Pub-Sub) pattern allows publishers to send messages to a topic, and multiple subscribers receive those messages independently.
In interviews, I explain that Pub-Sub decouples message producers from consumers in both time and space. Publishers do not need to know who the subscribers are, and subscribers can process messages at their own pace.
This pattern is widely used in event-driven microservices for notifications, analytics, and real-time updates. Tools like Kafka naturally implement Pub-Sub semantics. While powerful, Pub-Sub systems require careful handling of message ordering, duplication, and consumer offsets.
⸻
17. What is the Request–Response Pattern and where is it used?
The Request–Response pattern involves a client sending a request and waiting for a corresponding response. It is the most familiar communication model, commonly implemented using REST or gRPC.
In interviews, I explain that this pattern is intuitive and easy to debug, making it suitable for query-based interactions such as fetching user data or validating inputs.
However, overusing Request–Response in microservices can lead to chatty communication and cascading failures. Therefore, it should be used selectively, often combined with API Gateways and resilience patterns. For long-running or non-critical workflows, asynchronous alternatives are preferred.
⸻
18. What is a Message Broker and what role does it play?
A message broker is an intermediary system that routes, stores, and delivers messages between producers and consumers. It enables asynchronous communication and decouples services.
In interviews, I explain that message brokers handle concerns such as message persistence, retries, delivery guarantees, and consumer scaling. They are essential for implementing event-driven and message-based architectures.
Popular brokers include Kafka and RabbitMQ. While brokers improve reliability and scalability, they introduce operational overhead and must be carefully monitored. Choosing the right broker depends on throughput, latency, and delivery requirements.
⸻
19. What is Remote Procedure Call (RPC) and how is it used in Microservices?
RPC allows a service to call a method on another service as if it were a local function call. In microservices, this is commonly implemented using gRPC.
In interviews, I explain that RPC provides strong typing, high performance, and contract-first development. It is well-suited for internal service-to-service communication where low latency is critical.
However, RPC can hide network complexity, making failures harder to reason about. Developers must explicitly handle timeouts and retries. RPC should be used carefully to avoid tight coupling and synchronous dependency chains.
⸻
20. What is Backend for Frontend (BFF) and why is it useful?
Backend for Frontend (BFF) is a pattern where separate backend services are created for different client types, such as web, mobile, or desktop applications.
In interviews, I explain that BFF optimizes APIs for specific client needs, reducing over-fetching or under-fetching of data. Each BFF aggregates data from multiple microservices and presents a tailored response.
BFF improves performance and developer productivity but increases the number of services to maintain. It is commonly used in systems with diverse clients and complex UI requirements.
21. What is the Database per Service pattern and why is it recommended?
The Database per Service pattern states that each microservice should own and manage its own database. No other service should directly access that database. This ensures strong service autonomy and loose coupling.
In interviews, I explain that this pattern prevents schema-level coupling between services. If multiple services share a database, a schema change in one service can break others, effectively creating a distributed monolith. With database per service, each service can choose the most suitable database technology, scale independently, and evolve without coordination.
In Java microservices, this pattern is fundamental to achieving true independence. While it introduces challenges such as data duplication and distributed transactions, those challenges are addressed using patterns like Saga and eventual consistency rather than breaking service boundaries.
⸻
22. Why is a Shared Database considered an Anti-Pattern in Microservices?
A shared database is considered an anti-pattern because it violates service autonomy and loose coupling. When multiple microservices read and write to the same database, they become tightly coupled at the data layer.
In interviews, I explain that shared databases lead to coordination-heavy deployments, where a schema change requires multiple teams to align. This negates one of the main benefits of microservices: independent deployment. It also makes it difficult to enforce bounded contexts and ownership.
While shared databases may appear simpler initially — especially when migrating from a monolith — they create long-term scalability and maintainability issues. A shared database often indicates that the system is still a monolith in disguise rather than a true microservices architecture.
⸻
23. What is the Saga Pattern and how does it manage distributed transactions?
The Saga Pattern manages distributed transactions by breaking them into a sequence of local transactions, each handled by a different microservice. If one step fails, compensating transactions are executed to undo previous steps.
In interviews, I explain that Saga replaces traditional ACID transactions with eventual consistency, which is more suitable for distributed systems. There are two main saga implementations: orchestration-based and choreography-based.
In Java microservices, sagas are commonly implemented using asynchronous messaging and event-driven architecture. While Saga improves scalability and availability, it increases complexity and requires careful design of compensating actions to ensure data integrity.
⸻
24. What is Two-Phase Commit (2PC) and why is it discouraged in Microservices?
Two-Phase Commit (2PC) is a distributed transaction protocol that ensures atomicity across multiple systems by coordinating commit or rollback operations.
In interviews, I explain that although 2PC provides strong consistency, it is poorly suited for microservices. It introduces tight coupling, blocking behavior, and performance bottlenecks. If the coordinator fails, participating services can remain locked, impacting availability.
Modern microservices architectures favor availability and scalability over strict consistency. As a result, patterns like Saga and eventual consistency are preferred. 2PC is sometimes acceptable in tightly controlled enterprise environments, but it contradicts the core principles of microservices.
⸻
25. What is CQRS and when should it be used?
CQRS (Command Query Responsibility Segregation) separates write operations (commands) from read operations (queries) into different models.
In interviews, I explain that CQRS is useful when read and write workloads have different performance, scaling, or data modeling requirements. For example, write operations may require strong validation, while read operations need optimized queries.
In Java microservices, CQRS is often combined with event-driven architecture. While CQRS improves scalability and performance, it adds architectural complexity and should only be used when there is a clear benefit, not as a default design choice.
⸻
26. Explain Event Sourcing and its benefits.
Event Sourcing stores the sequence of events that led to the current state of an entity rather than storing the current state directly. The current state is reconstructed by replaying events.
In interviews, I explain that Event Sourcing provides a complete audit trail, supports time-travel debugging, and integrates naturally with event-driven systems. It is especially useful in domains like finance and insurance.
However, Event Sourcing adds complexity in event versioning, schema evolution, and querying current state. It is often combined with CQRS to optimize reads. In Java ecosystems, it requires strong discipline and tooling to be successful.
⸻
27. What is Polyglot Persistence and why is it important?
Polyglot persistence means using different database technologies for different microservices based on their specific needs. For example, a service may use a relational database, while another uses a document or key-value store.
In interviews, I explain that polyglot persistence supports service autonomy and optimizes performance. Each microservice chooses the data store that best fits its access patterns and consistency requirements.
However, it introduces operational complexity, as teams must manage multiple database technologies. This pattern is powerful but requires mature DevOps practices and strong governance to avoid operational chaos.
⸻
28. What is Data Replication and how is it used in Microservices?
Data replication involves maintaining copies of data across services or storage systems to improve performance, availability, or fault tolerance.
In interviews, I explain that replication is often used to support read-heavy workloads or enable local reads in different services. For example, a reporting service may replicate data from transactional services.
Replication must be handled carefully to avoid stale data and inconsistency. It is commonly combined with eventual consistency and event-driven updates. While replication improves performance, it increases storage costs and synchronization complexity.
⸻
29. What is Idempotency and why is it critical in Microservices?
Idempotency means that repeating the same operation multiple times produces the same result. This is critical in distributed systems where retries may occur due to network failures.
In interviews, I explain that idempotency prevents duplicate side effects, such as double payments or duplicate orders. It is commonly implemented using unique request IDs, deduplication keys, or version checks.
Idempotency is especially important in asynchronous messaging and REST APIs. Without it, retries can corrupt data. Designing idempotent operations significantly improves reliability and resilience in microservices.
⸻
30. What does Eventually Consistent Data mean in practice?
Eventually consistent data means that different services may temporarily see different versions of data, but the system will converge to a consistent state over time.
In interviews, I explain that eventual consistency is a trade-off that enables scalability and availability in distributed systems. It requires careful design of user experience, business workflows, and compensating actions.
For example, an order may be confirmed before inventory is fully updated. Systems must handle these transient inconsistencies gracefully. Eventual consistency is not a weakness — it is a deliberate design choice aligned with the realities of distributed computing.
⸻
31. What is the Circuit Breaker pattern and why is it important in Microservices?
The Circuit Breaker pattern prevents a microservice from repeatedly calling another service that is failing or slow. It works like an electrical circuit breaker: when failures cross a threshold, the circuit opens and blocks calls for a defined period.
In interviews, I explain that without circuit breakers, synchronous service calls can cause cascading failures, where one slow service degrades the entire system. A circuit breaker typically has three states: Closed, Open, and Half-Open. In the half-open state, a limited number of test requests determine whether the service has recovered.
In Java microservices, this is commonly implemented using Resilience4j. Circuit breakers improve system stability and user experience but must be tuned carefully to avoid false positives or unnecessary traffic blocking.
⸻
32. Explain the Retry Pattern and its risks.
The Retry Pattern automatically retries a failed operation, assuming the failure is temporary, such as a transient network issue or momentary service unavailability.
In interviews, I stress that retries must be applied carefully. Blind retries can worsen outages by increasing load on already struggling services. This is known as the retry storm problem. Best practices include exponential backoff, jitter, and retry limits.
Retries are commonly used with idempotent operations, such as GET requests or safely designed POSTs. In Java microservices, retry mechanisms are often combined with circuit breakers to prevent overload. When implemented correctly, retries increase reliability; when misused, they can amplify failures.
⸻
33. What is the Timeout Pattern and why is it essential?
The Timeout Pattern defines a maximum duration a service will wait for a response before failing fast. This prevents threads from being blocked indefinitely.
In interviews, I explain that timeouts are essential in distributed systems because networks are unreliable. Without timeouts, one slow dependency can exhaust thread pools and bring down a service.
Timeouts should be tuned based on service SLAs and latency expectations. They are often combined with circuit breakers and fallbacks. In Java microservices, timeouts are configured at HTTP client, database, and messaging levels. A well-defined timeout strategy significantly improves system responsiveness and stability.
⸻
34. Explain the Bulkhead Pattern with a real-world analogy.
The Bulkhead Pattern isolates components so that failure in one part of the system does not impact others. The name comes from ship design, where compartments prevent flooding from sinking the entire ship.
In interviews, I explain that bulkheads in microservices typically isolate thread pools, connection pools, or resources per dependency. For example, calls to a slow payment service should not consume all threads needed for order processing.
In Java microservices, bulkheads are often implemented using separate thread pools via Resilience4j. Bulkheading improves fault isolation and resilience, but it requires careful resource planning to avoid underutilization.
⸻
35. What is the Fallback Pattern and how does it improve resilience?
The Fallback Pattern provides an alternative response when a service call fails. Instead of returning an error, the system returns cached data, default values, or a graceful message.
In interviews, I explain that fallbacks improve user experience and system availability. For example, if a recommendation service fails, the application can return trending products instead of an error.
Fallbacks should be meaningful and safe; returning incorrect data can be worse than returning no data. In Java microservices, fallbacks are commonly configured alongside circuit breakers. While not a replacement for fixing failures, fallbacks help systems degrade gracefully.
⸻
36. What is Rate Limiting and why is it needed?
Rate limiting restricts the number of requests a client or service can make within a defined time window.
In interviews, I explain that rate limiting protects services from abuse, traffic spikes, and denial-of-service scenarios. It also ensures fair usage among clients.
Rate limiting is commonly enforced at the API Gateway level and can be based on IP address, user ID, or API key. While it improves stability and security, rate limits must be designed carefully to avoid impacting legitimate users. Proper monitoring and tuning are essential.
⸻
37. How is Throttling different from Rate Limiting?
Throttling controls the speed of request processing, while rate limiting controls the number of requests allowed.
In interviews, I explain that throttling smooths traffic by slowing down request handling when systems are under pressure, rather than rejecting requests outright. This is useful for protecting backend systems during load spikes.
Throttling improves system stability but may increase response times. It is often used together with rate limiting to balance performance and protection. In microservices, throttling can be implemented at gateways or load balancers.
⸻
38. What is Load Balancing and how does it contribute to fault tolerance?
Load balancing distributes incoming requests across multiple service instances to improve availability, scalability, and fault tolerance.
In interviews, I explain that load balancers detect unhealthy instances and route traffic only to healthy ones. This prevents a single instance failure from impacting the entire service.
Load balancing can be client-side or server-side. In cloud-native Java microservices, platforms like Kubernetes provide built-in load balancing. Effective load balancing ensures optimal resource utilization and resilience.
⸻
39. What is Graceful Degradation in Microservices?
Graceful degradation means that when parts of the system fail, the application continues to operate with reduced functionality rather than failing completely.
In interviews, I explain that graceful degradation focuses on user experience. For example, if analytics or recommendations are unavailable, core checkout functionality should still work.
This is achieved using fallbacks, feature toggles, and prioritization of critical services. Graceful degradation improves system reliability perception and aligns with real-world business needs where partial availability is better than total outage.
⸻
40. Explain the Fail-Fast Pattern and when to use it.
The Fail-Fast Pattern encourages systems to detect failures early and stop processing immediately rather than attempting to continue in an unstable state.
In interviews, I explain that fail-fast behavior simplifies debugging and prevents resource exhaustion. For example, if a required dependency is unavailable at startup, the service should fail immediately rather than running in a degraded state.
Fail-fast is commonly used during service startup, configuration validation, and critical dependency checks. While it reduces ambiguity, it must be balanced with resilience patterns to avoid unnecessary downtime in recoverable scenarios.
⸻
41. What is Authentication in Microservices and how is it typically implemented?
Authentication is the process of verifying the identity of a user or system before granting access to a microservice. In microservices architecture, authentication is usually centralized rather than implemented individually by every service.
In interviews, I explain that authentication is commonly handled at the API Gateway or Identity Provider level, ensuring backend services remain focused on business logic. Clients authenticate once and receive a token, which is then passed to downstream services.
In Java-based systems, frameworks like Spring Security are widely used. Authentication mechanisms include username/password, certificates, OAuth-based flows, or federated identity providers. Centralized authentication improves consistency, reduces duplication, and simplifies security management across microservices.
⸻
42. What is Authorization and how does it differ from Authentication?
Authorization determines what an authenticated user is allowed to do, whereas authentication determines who the user is. These are related but distinct security concerns.
In interviews, I emphasize that authentication always happens first, followed by authorization. For example, a user may be authenticated successfully but still not authorized to access certain resources or perform specific actions.
In microservices, authorization is often implemented using role-based access control (RBAC) or attribute-based access control (ABAC). Authorization decisions can be enforced at the API Gateway, service level, or both. Fine-grained authorization is especially important in distributed systems to prevent privilege escalation and ensure compliance with business rules.
⸻
43. Explain OAuth2 and OpenID Connect in Microservices.
OAuth2 is an authorization framework that allows applications to access resources on behalf of users without sharing credentials. OpenID Connect (OIDC) builds on OAuth2 to provide authentication and identity information.
In interviews, I explain that OAuth2 handles authorization (access tokens), while OpenID Connect adds authentication (ID tokens). Together, they enable secure, scalable identity management across distributed systems.
In Java microservices, OAuth2 and OIDC are commonly used with external identity providers and integrated via Spring Security. They support single sign-on (SSO), token-based access, and federation with third-party providers. These standards are essential for modern microservices, especially in enterprise and cloud-native environments.
⸻
44. What is JWT (JSON Web Token) and why is it widely used?
JWT is a compact, self-contained token format used to securely transmit claims between parties. It is commonly used to carry authentication and authorization information in microservices.
In interviews, I explain that JWTs are stateless, meaning services can validate tokens without calling a central session store. This makes JWT ideal for horizontally scalable systems. A JWT typically contains claims such as user identity, roles, and expiration time, and is cryptographically signed to prevent tampering.
However, JWTs must be handled carefully. Token expiration, revocation, and size considerations are important. Overusing JWTs for sensitive data is discouraged. When used correctly, JWTs simplify security and improve performance in microservices.
⸻
45. What is Zero Trust Security and why is it important for Microservices?
Zero Trust Security is a model where no request is trusted by default, even if it originates from within the network. Every request must be authenticated, authorized, and validated.
In interviews, I explain that traditional perimeter-based security fails in microservices because services communicate over dynamic, distributed environments. Zero Trust assumes breaches can happen and focuses on minimizing blast radius.
Zero Trust principles include mutual TLS, token-based authentication, least privilege access, and continuous verification. In microservices, this approach significantly improves security posture, especially in cloud and containerized environments. While it increases complexity, Zero Trust is becoming a standard for modern, security-sensitive systems.
PART 6: Observability & Operations (46–50)
⸻
46. What is Centralized Logging and why is it essential in Microservices?
Centralized logging is the practice of collecting logs from all microservices into a single, searchable system. In a distributed architecture, logs are scattered across containers, nodes, and services, making local log inspection ineffective.
In interviews, I explain that centralized logging enables faster troubleshooting, correlation across services, and operational visibility. When a user request flows through multiple services, centralized logs allow engineers to trace errors end-to-end. Logs are typically structured (JSON) and enriched with metadata such as service name, environment, and correlation IDs.
Common stacks include ELK Stack or cloud-native alternatives. While logging is critical, excessive logging can increase costs and noise, so teams must balance verbosity with value and enforce log retention policies.
⸻
47. What is Distributed Tracing and how does it work?
Distributed tracing tracks a single request as it travels across multiple microservices, capturing latency, errors, and dependencies at each hop. It provides a visual representation of request flow and timing.
In interviews, I explain that tracing solves a key microservices problem: “Where is the latency or failure actually occurring?” Each request carries a trace ID, and each service records spans representing work done. These spans are then assembled into a trace.
Distributed tracing is commonly implemented using OpenTelemetry with backends like Jaeger or Zipkin. Tracing greatly improves root-cause analysis but requires consistent instrumentation and sampling strategies to control overhead in high-traffic systems.
⸻
48. What are Metrics and Monitoring in Microservices?
Metrics are numerical measurements that represent system behavior over time, such as request rate, latency, error rate, CPU usage, and memory consumption. Monitoring is the practice of collecting, visualizing, and alerting on these metrics.
In interviews, I emphasize that metrics answer “Is the system healthy right now?” rather than “What happened?” Metrics are aggregated and lightweight, making them ideal for real-time alerts and dashboards.
In Java microservices, metrics are often exposed via Micrometer and scraped by Prometheus, with dashboards in Grafana. Effective monitoring requires defining SLIs, SLOs, and actionable alerts to avoid alert fatigue.
⸻
49. What are Health Checks and how are they used operationally?
Health checks are endpoints that report the current health status of a service. They are used by orchestrators and load balancers to decide whether traffic should be sent to a service instance.
In interviews, I explain that there are typically two types:
• Liveness checks: Is the service running?
• Readiness checks: Is the service ready to receive traffic?
In Java microservices, health checks often validate dependencies such as databases or message brokers. Platforms like Kubernetes rely heavily on health checks to restart unhealthy pods and manage rolling deployments. Poorly designed health checks can cause cascading restarts, so they must be lightweight and reliable.
⸻
50. What is Auto Scaling and how does it benefit Microservices?
Auto scaling automatically adjusts the number of service instances based on demand. It ensures that systems can handle traffic spikes while optimizing resource usage during low demand.
In interviews, I explain that auto scaling is a key benefit of stateless microservices. Scaling decisions are usually driven by metrics such as CPU usage, memory consumption, or request rate. Horizontal scaling (adding instances) is more common than vertical scaling.
In cloud-native Java microservices, Kubernetes Horizontal Pod Autoscaler is widely used. Auto scaling improves availability and cost efficiency but requires proper capacity planning and load testing. Without limits, auto scaling can amplify failures or increase costs.
1. What is Unit Testing and why is it important?
Unit testing is the practice of testing the smallest testable units of code, usually individual methods or classes, in isolation. The goal is to verify that each unit behaves as expected under various conditions.
In interviews, I explain that unit testing improves code quality, confidence, and maintainability. It allows developers to catch bugs early, reduces regression issues, and enables safe refactoring. Well-written unit tests also serve as living documentation for the codebase.
In Java ecosystems, unit testing is typically done using JUnit along with mocking libraries like Mockito. Unit tests should be fast, deterministic, and independent of external systems like databases or network calls.
⸻
2. What is JUnit and how does it support unit testing?
JUnit is the most widely used unit testing framework for Java. It provides annotations, assertions, and test runners to write and execute automated tests.
In interviews, I explain that JUnit allows developers to define test methods using annotations like @Test, set up test data using @BeforeEach, and clean up using @AfterEach. JUnit 5 introduced a modular architecture with improved extensions, parameterized tests, and better IDE integration.
JUnit promotes test isolation, repeatability, and automation. It integrates seamlessly with build tools like Maven and Gradle, making it a foundational tool for CI/CD pipelines in Java projects.
⸻
3. What is the difference between Unit Testing and Integration Testing?
Unit testing focuses on testing individual components in isolation, while integration testing verifies how multiple components work together.
In interviews, I explain that unit tests mock all external dependencies, such as databases or APIs, ensuring fast execution. Integration tests, on the other hand, may involve real databases, message brokers, or service calls.
Unit tests help identify logic errors early, while integration tests validate configuration and wiring. Both are essential, but unit tests should be far more numerous because they are faster, cheaper, and easier to maintain.
⸻
4. What is Mocking and why is it required?
Mocking is the technique of creating fake implementations of dependencies to isolate the unit under test.
In interviews, I explain that mocking allows us to:
• Control dependency behavior
• Simulate failures
• Avoid slow or unavailable external systems
For example, when testing a service class, we mock repositories or external API clients. Mockito enables this by creating mocks at runtime and defining expected behavior. Mocking ensures that unit tests are deterministic and focus only on the logic being tested.
⸻
5. What is Mockito and how does it work?
Mockito is a popular Java mocking framework used to create mock objects for unit testing.
In interviews, I explain that Mockito works by generating proxy objects that mimic real dependencies. Developers can define expected behavior using when().thenReturn() and verify interactions using verify().
Mockito promotes clean tests by avoiding heavy setup and boilerplate code. It integrates seamlessly with JUnit and supports advanced features such as argument matchers, spies, and behavior verification.
⸻
6. What is the difference between Mock and Stub?
A stub provides predefined responses to method calls, while a mock also verifies how the method was called.
In interviews, I explain that stubs are passive — they return values but do not assert behavior. Mocks are active — they validate interactions such as method calls, arguments, and invocation count.
Mockito supports both concepts using the same API. Understanding the difference helps write clearer tests and avoid over-verification, which can make tests brittle.
⸻
7. What are Spies in Mockito and when should they be used?
A spy wraps a real object and allows selective mocking of its methods.
In interviews, I explain that spies are useful when we want to mock only part of an object’s behavior while keeping the rest intact. For example, when testing legacy code or complex classes where full mocking is impractical.
However, spies should be used sparingly. They can make tests harder to understand and may unintentionally call real methods, leading to side effects.
⸻
8. What is Test Isolation and why does it matter?
Test isolation means that each test runs independently and does not rely on shared state.
In interviews, I explain that isolated tests are predictable and reliable. Tests should not depend on execution order or data created by other tests.
Mocking, proper setup/teardown, and avoiding static/shared state are key techniques to achieve isolation. Without isolation, tests may pass locally but fail in CI environments.
⸻
9. What are Assertions and why are they important?
Assertions validate the expected outcome of a test.
In interviews, I explain that assertions are the core of unit testing — they determine whether a test passes or fails. JUnit provides assertions like assertEquals, assertThrows, and assertTrue.
Good assertions are precise and expressive. Poor assertions, such as asserting too many things in one test, reduce clarity and make failures harder to diagnose.
⸻
10. What is @BeforeEach and @AfterEach used for?
These JUnit annotations are used to set up and clean up test data before and after each test method.
In interviews, I explain that @BeforeEach initializes objects or mocks, while @AfterEach releases resources or resets state. They help avoid duplication and improve test readability.
Proper setup and cleanup ensure consistent test behavior and prevent state leakage between tests.
⸻
11. What are Parameterized Tests in JUnit?
Parameterized tests allow running the same test logic with different input values.
In interviews, I explain that this reduces duplication and improves coverage. JUnit 5 supports parameterized tests using annotations like @ParameterizedTest and @ValueSource.
They are especially useful for validating edge cases and boundary conditions in business logic.
⸻
12. What is Code Coverage and does high coverage mean good tests?
Code coverage measures the percentage of code executed during tests.
In interviews, I emphasize that high coverage does not guarantee quality. Tests can execute code without asserting correct behavior.
Coverage is a useful metric but should not be the primary goal. Meaningful assertions and scenario coverage matter more than raw numbers.
⸻
13. What is TDD (Test-Driven Development)?
TDD is a development approach where tests are written before the production code.
In interviews, I explain the Red–Green–Refactor cycle:
1. Write a failing test
2. Write minimal code to pass
3. Refactor safely
TDD improves design, encourages loosely coupled code, and ensures high test coverage. However, it requires discipline and experience to apply effectively.
⸻
14. How do you test exceptions in JUnit?
Exceptions are tested by asserting that a method throws the expected exception.
In interviews, I explain that JUnit provides assertThrows, which verifies both the exception type and optionally the message.
Testing exceptions ensures that error conditions are handled gracefully and contractually.
⸻
15. What is Behavior Verification in Mockito?
Behavior verification checks whether specific methods were called with expected arguments.
In interviews, I explain that Mockito’s verify() ensures correct interaction between components. It is useful when behavior matters more than return values.
However, excessive verification leads to brittle tests tightly coupled to implementation details.
⸻
16. What are Argument Matchers in Mockito?
Argument matchers allow flexible matching of method arguments.
In interviews, I explain that matchers like any(), eq(), and contains() make tests resilient to irrelevant changes. They improve readability but must be used consistently.
Improper use of matchers can cause unexpected failures or invalid tests.
⸻
17. What is the difference between @Mock and @InjectMocks?
@Mock creates mock dependencies, while @InjectMocks injects those mocks into the class under test.
In interviews, I explain that @InjectMocks simplifies setup by automatically wiring mocks via constructor or field injection.
This reduces boilerplate and keeps tests focused on behavior rather than configuration.
⸻
18. How do you unit test legacy code?
Legacy code often lacks testability due to tight coupling or static dependencies.
In interviews, I explain that the strategy is to introduce seams gradually using refactoring, interfaces, and partial mocking. Spies and wrapper classes can help isolate logic.
The goal is incremental improvement without breaking existing functionality.
⸻
19. What makes a good unit test?
A good unit test is fast, isolated, readable, and deterministic.
In interviews, I explain that good tests follow the AAA pattern:
• Arrange
• Act
• Assert
They test one behavior, use meaningful names, and fail for a single reason. Maintainable tests are as important as maintainable production code.
⸻
20. What are common mistakes in unit testing?
Common mistakes include over-mocking, testing implementation details, and writing brittle tests.
In interviews, I explain that unit tests should focus on behavior, not internal structure. Overusing mocks or assertions leads to fragile tests that break during refactoring.
Good unit testing balances coverage, clarity, and maintainability.
What are SQL JOINs and how many types are there?
SQL JOINs combine rows from two or more tables based on related columns. There are several types:
- INNER JOIN: returns matching rows only.
- LEFT (OUTER) JOIN: returns all left table rows plus matches.
- RIGHT (OUTER) JOIN: all right table rows plus matches.
- FULL (OUTER) JOIN: all rows from both sides.
- CROSS JOIN: Cartesian product.
In interviews, I explain that JOINs are essential for modeling relationships. INNER JOIN is most common when you only need related records; LEFT JOIN is widely used to include unmatched rows with NULLs.
2. Write a query to find employees who have not made any sales.
SELECT e.employee_id, e.first_name, e.last_name
FROM employees e
LEFT JOIN sales s
ON e.employee_id = s.employee_id
WHERE s.sale_id IS NULL;3. What is the difference between WHERE and HAVING?
WHERE filters rows before aggregation, and HAVING filters after grouping.
SELECT department, COUNT(*) AS cnt
FROM employees
WHERE salary > 3000
GROUP BY department
HAVING COUNT(*) > 5;4. Write a query to find the department with the highest average salary.
SELECT department, AVG(salary) avg_sal
FROM employees
GROUP BY department
ORDER BY avg_sal DESC
LIMIT 1;5. What is a Subquery and when should it be used?
A subquery is a query inside another query. They help in filtering, comparing, or transforming data.
SELECT employee_id, first_name
FROM employees
WHERE salary > (
SELECT AVG(salary)
FROM employees
);6. Difference between correlated and uncorrelated subqueries?
- Uncorrelated runs once: independent of outer query.
- Correlated references columns from outer query and runs per row
SELECT e1.employee_id
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e2.department = e1.department
);7. SQL: Find the second highest salary without using LIMIT.
SELECT MAX(salary) AS SecondHighest
FROM employees
WHERE salary < (
SELECT MAX(salary) FROM employees
);8. How does INNER JOIN behave with duplicates?
If both tables have duplicate matching values, the INNER JOIN returns Cartesian combinations for those matches.
SELECT e.employee_id, p.project_id
FROM employees e
INNER JOIN project_assignments p
ON e.employee_id = p.employee_id;9. What is the use of CROSS JOIN?
A CROSS JOIN returns the Cartesian product of two tables.
SELECT employee_id, project_id
FROM employees
CROSS JOIN projects;10. Write a query using EXISTS and explain its use.
EXISTS checks whether any row matches the condition. It’s efficient for presence checks.
SELECT employee_id, first_name
FROM employees e
WHERE EXISTS (
SELECT 1
FROM sales s
WHERE s.employee_id = e.employee_id
);11. SQL: Find employees whose salary is above the average salary of their department.
SELECT e.employee_id, e.first_name, e.salary
FROM employees e
WHERE salary > (
SELECT AVG(salary)
FROM employees
WHERE department = e.department
);12. What is a Self Join? Give an example.
SELECT e1.employee_id, e1.manager_id, e2.first_name AS manager_name
FROM employees e1
LEFT JOIN employees e2
ON e1.manager_id = e2.employee_id;13. Explain UNION vs UNION ALL.
- UNION removes duplicates.
- UNION ALL keeps all rows, including duplicates.
SELECT employee_id FROM employees
UNION
SELECT contractor_id FROM contractors;14. What are Window Functions? Provide an example.
Window functions perform calculations across related rows without collapsing result sets.
SELECT employee_id,
salary,
RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees;15. SQL: Find the top 3 salaries per department.
SELECT department, employee_id, salary
FROM (
SELECT department, employee_id, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
) x
WHERE rn <= 3;16. How do you optimize a slow SQL query?
Common techniques:
- Add proper indexes
- Avoid SELECT *; return only needed columns
- Use joins instead of subqueries when appropriate
- Avoid functions on indexed columns
- Use EXPLAIN PLAN to analyze execution paths
In interviews, explain that query tuning is crucial for performance, especially in large datasets.
17. Write a query to calculate cumulative sum of sales by date.
SELECT order_date,
SUM(amount) OVER (ORDER BY order_date) AS cumulative_sales
FROM sales;18. Write SQL to delete duplicate rows (keeping one).
DELETE FROM employees
WHERE employee_id NOT IN (
SELECT MIN(employee_id)
FROM employees
GROUP BY first_name, last_name
);19. Explain INNER JOIN vs LEFT JOIN with example.
INNER JOIN returns only matching rows:
SELECT e.id, d.name
FROM employees e
INNER JOIN departments d
ON e.dept_id = d.dept_id;LEFT JOIN returns all left table rows + matches:
SELECT e.id, d.name
FROM employees e
LEFT JOIN departments d
ON e.dept_id = d.dept_id;LEFT JOIN returns employees even if they lack department entries.
20. Write SQL to find employees hired in the last 30 days.
SELECT employee_id, first_name, hire_date
FROM employees
WHERE hire_date >= CURRENT_DATE - INTERVAL '30' DAY;Customers Who Never Placed an Order
Tables:
Customers(customer_id, name)
Orders(order_id, customer_id)
SELECT c.customer_id, c.name
FROM Customers c
LEFT JOIN Orders o
ON c.customer_id = o.customer_id
WHERE o.order_id IS NULL;Nth Highest Salary
Replace N with arbitrary rank.
Get Kaizen Chandra’s stories in your inbox
Join Medium for free to get updates from this writer.
Query (example for 3rd highest):
SELECT DISTINCT salary
FROM Employee
ORDER BY salary DESC
LIMIT 1 OFFSET 2;Department With Highest Average Salary
SELECT d.dept_name
FROM Department d
JOIN Employee e
ON d.dept_id = e.dept_id
GROUP BY d.dept_name
ORDER BY AVG(e.salary) DESC
LIMIT 1;Find Customers Who Ordered All Products
SELECT o.customer_id
FROM Orders o
JOIN OrderItems oi
ON o.order_id = oi.order_id
GROUP BY o.customer_id
HAVING COUNT(DISTINCT oi.product_id) = (
SELECT COUNT(*) FROM Products
);Find the Most Recent Order per Customer
SELECT o.customer_id, o.order_id
FROM Orders o
JOIN (
SELECT customer_id, MAX(order_date) AS max_date
FROM Orders
GROUP BY customer_id
) m
ON o.customer_id = m.customer_id AND o.order_date = m.max_date;Employees Not Working on Projects
SELECT e.emp_id
FROM Employee e
LEFT JOIN ProjectAssignments pa
ON e.emp_id = pa.emp_id
WHERE pa.project_id IS NULL;Sales Growth by Month
SELECT month,
sales,
LAG(sales, 1) OVER (ORDER BY month) AS prev_month,
sales - LAG(sales, 1) OVER (ORDER BY month) AS growth
FROM MonthlySales;1. What is ORM and how does Hibernate implement ORM?
ORM (Object Relational Mapping) is a technique that maps Java objects to relational database tables. Hibernate is a mature ORM framework that automates this mapping, allowing developers to work with Java objects instead of SQL queries. Hibernate maps Java classes to tables, class fields to columns, and object relationships to foreign keys using annotations or XML. Internally, Hibernate uses JDBC to generate optimized SQL at runtime. ORM reduces boilerplate code, improves maintainability, and ensures database portability. Hibernate also manages entity state transitions such as transient, persistent, detached, and removed, which makes transaction handling consistent and reliable.
⸻
2. Explain @Entity and @Table annotations.
@Entity marks a Java class as a persistent Hibernate entity, meaning it will be mapped to a database table. Without this annotation, Hibernate ignores the class. The @Table annotation is optional and is used to customize table-level metadata such as table name, schema, catalog, and unique constraints. If @Table is not provided, Hibernate uses the class name as the table name by default. In enterprise projects, @Table is commonly used to align with existing database naming conventions or schemas.
⸻
3. What is the difference between @Column and @Basic?
@Basic is the simplest mapping and is implicitly applied to all non-transient fields. It defines basic property behavior like optional and fetch type. @Column, on the other hand, maps a field explicitly to a database column and allows configuration such as column name, length, nullable, unique, precision, and scale. In real projects, @Column is preferred because it gives better control over database schema and improves readability and maintainability.
⸻
4. Explain @Id and identifier generation strategies.
@Id marks a field as the primary key of an entity. Hibernate provides several identifier generation strategies using @GeneratedValue. Common strategies include IDENTITY (auto-increment, DB-driven), SEQUENCE (sequence-based, efficient for Oracle/Postgres), TABLE (less common, uses a separate table), and AUTO (Hibernate decides based on dialect). Choosing the right strategy impacts performance and scalability. In microservices, SEQUENCE or UUID-based strategies are often preferred to avoid ID collisions.
⸻
5. What is @Embedded and @Embeddable?
@Embeddable is used to define a reusable value object whose fields are mapped to the owning entity’s table. @Embedded is used inside an entity to include that embeddable object. The embedded object does not have its own table or identity. This is commonly used for grouping related fields like address, audit details, or contact info, improving domain modeling while keeping the database normalized.
⸻
6. Difference between @OneToOne and @ManyToOne.
@OneToOne represents a one-to-one relationship between two entities, often sharing a foreign key or primary key. @ManyToOne represents a many-to-one relationship where multiple entities reference a single parent entity. @ManyToOne is more common in real-world systems like orders to customers. @OneToOne should be used carefully because it can introduce unnecessary joins and performance overhead if misused.
⸻
7. Explain @OneToMany mapping and its ownership.
@OneToMany defines a parent-child relationship where one parent has multiple children. By default, the child side (@ManyToOne) is the owning side because it contains the foreign key. The mappedBy attribute is used on the parent side to indicate that it is not the owner. Proper ownership mapping avoids extra join tables and improves performance. Bidirectional relationships must be carefully managed to avoid infinite loops in serialization.
⸻
8. Difference between bidirectional and unidirectional mappings.
In unidirectional mapping, only one entity knows about the relationship, making it simpler but less flexible. Bidirectional mapping allows navigation from both sides, which is useful for complex business logic. However, bidirectional mappings require careful synchronization of both sides of the relationship to avoid data inconsistency. In interviews, it’s important to mention that bidirectional mappings should be used only when navigation is truly required.
⸻
9. What is mappedBy?
mappedBy indicates that the current entity is not the owner of the relationship. It points to the field name on the owning side. This avoids redundant foreign keys or join tables. Using mappedBy correctly is critical for maintaining clean schema design and preventing Hibernate from creating unnecessary tables. It also improves performance by reducing SQL complexity.
⸻
10. Explain @JoinColumn and @JoinTable.
@JoinColumn defines the foreign key column used in a relationship. It is the most common and efficient approach. @JoinTable is used when an intermediate join table is required, such as in many-to-many relationships. While @JoinTable provides flexibility, it increases join complexity. In performance-sensitive systems, @JoinColumn is preferred whenever possible.
⸻
11. What is @ManyToMany and when should it be avoided?
@ManyToMany represents a relationship where multiple entities relate to multiple others using a join table. While easy to implement, it becomes difficult to extend when additional attributes are needed on the relationship. In real-world systems, many-to-many relationships are often replaced with two one-to-many relationships and an explicit join entity to maintain flexibility and scalability.
⸻
12. Explain cascading in Hibernate.
Cascading defines how operations on a parent entity propagate to child entities. Common cascade types include PERSIST, MERGE, REMOVE, REFRESH, and ALL. Cascading simplifies code by reducing explicit repository calls. However, incorrect cascade settings can lead to accidental deletions or performance issues. In interviews, always mention that cascade should reflect business ownership, not just database relationships.
⸻
13. What is orphan removal?
orphanRemoval = true ensures that child entities are deleted when they are removed from the parent collection. It is commonly used in one-to-many relationships where the parent fully owns the child lifecycle. Orphan removal is stronger than cascade remove and should be used carefully, as it results in automatic DELETE statements when children are disassociated.
⸻
14. Difference between FetchType.LAZY and FetchType.EAGER.
LAZY loading fetches related entities only when accessed, improving performance and reducing memory usage. EAGER loading fetches relationships immediately. While EAGER may seem convenient, it often causes performance bottlenecks and N+1 query issues. Best practice is to use LAZY by default and control fetching explicitly using fetch joins or entity graphs.
⸻
15. What is the N+1 problem and how is it related to mappings?
The N+1 problem occurs when Hibernate executes one query to fetch parent entities and N additional queries for each child entity due to lazy loading. This is often caused by improper mapping or fetch strategy. It can be solved using fetch joins, batch fetching, or entity graphs. Interviewers expect candidates to explain both the cause and solution clearly.
@EntityGraph and JOIN FETCH and @BatchSize, Hibernate Global Batch Configuration
⸻
16. Explain @Inheritance strategies.
Hibernate supports inheritance mapping using SINGLE_TABLE, JOINED, and TABLE_PER_CLASS. SINGLE_TABLE is fastest but uses nullable columns. JOINED provides normalization but requires joins. TABLE_PER_CLASS avoids joins but duplicates columns. The choice depends on performance, normalization, and query complexity. In interviews, mentioning trade-offs is critical.
⸻
17. What is @MappedSuperclass?
@MappedSuperclass allows common fields to be shared across multiple entities without creating a separate table. Fields are mapped directly into child entity tables. It is commonly used for audit fields like createdDate and updatedBy. Unlike inheritance, mapped superclasses are not queryable entities.
⸻
18. Difference between @Transient and transient.
@Transient tells Hibernate not to persist a field, while the Java transient keyword prevents serialization. Both prevent persistence, but @Transient is Hibernate-specific and more explicit. In enterprise applications, @Transient is preferred for clarity and ORM control.
⸻
19. What is @Version and optimistic locking?
@Version enables optimistic locking by maintaining a version column. Hibernate checks the version before updating a row to prevent lost updates in concurrent transactions. If the version changes, Hibernate throws an exception. Optimistic locking is widely used in high-concurrency applications because it avoids database locks and improves scalability.
⸻
20. Best practices for Hibernate ORM mappings?
Best practices include using LAZY fetching by default, avoiding unnecessary bidirectional mappings, preferring @ManyToOne over @OneToOne, handling cascading carefully, and modeling relationships based on business ownership. Always test queries using real data volumes. Proper mapping design has a direct impact on performance, maintainability, and scalability in production systems.
21. What are the different states of a Hibernate entity?
Hibernate entities go through four main lifecycle states: Transient, Persistent, Detached, and Removed. A transient object is created using new and is not associated with any session. When the entity is associated with a Hibernate session, it becomes persistent and any changes are automatically tracked and synchronized with the database. Once the session is closed, the entity becomes detached, meaning changes are no longer tracked. Removed entities are scheduled for deletion. Understanding entity states is critical to avoid issues like unintended updates or LazyInitializationException.
⸻
22. Difference between Session.save(), persist(), and saveOrUpdate()?
save() immediately assigns an identifier and returns it, even before flushing. persist() makes the entity persistent but does not return an identifier and is compliant with JPA standards. saveOrUpdate() decides whether to insert or update based on entity state. In modern Spring Boot applications, persist() and merge() are preferred due to JPA compliance and cleaner lifecycle management.
⸻
23. What is merge() and how is it different from update()?
update() reattaches a detached entity to the current session and fails if another instance with the same identifier already exists. merge() copies the state of a detached entity into a managed instance and returns the managed object. merge() is safer in multi-layered applications like REST APIs where entities often travel across boundaries.
⸻
24. Explain LazyInitializationException.
This exception occurs when a lazily loaded association is accessed outside an active Hibernate session. It typically happens when entities are fetched in the service layer but accessed in the controller layer. Solutions include using fetch joins, DTO projections, Open Session in View (with caution), or entity graphs. Interviewers expect candidates to explain both cause and multiple solutions.
⸻
25. What is the difference between get() and load()?
get() immediately hits the database and returns null if the entity does not exist. load() returns a proxy and throws an exception if the entity is accessed and not found. load() is useful when you are sure the entity exists and want to avoid an immediate database hit.
⸻
26. What is Hibernate proxy?
A proxy is a subclass generated by Hibernate to support lazy loading. It delays database access until a method on the entity is accessed. Proxies improve performance but can cause issues with serialization, equals()/hashCode(), and JSON conversion if not handled properly.
⸻
27. Explain first-level and second-level cache.
First-level cache is session-scoped and mandatory; it stores entities within a Hibernate session. Second-level cache is optional and shared across sessions. It improves performance for frequently accessed data. Interviewers often expect candidates to explain when not to use second-level cache, such as for frequently changing data.
⸻
28. What is query cache?
Query cache stores the result of queries, not entities themselves. It works with second-level cache and must be explicitly enabled. While it can improve performance, it is risky for dynamic queries and should be used selectively.
⸻
29. Difference between HQL and Criteria API.
HQL is string-based and closer to SQL but operates on entities. Criteria API is type-safe and programmatic, reducing runtime errors. Criteria is preferred for dynamic queries, while HQL is simpler for static queries.
⸻
30. What is native SQL in Hibernate?
Hibernate allows native SQL queries when complex or DB-specific operations are needed. Native queries bypass Hibernate’s abstraction layer, so developers must map results manually. They should be used sparingly due to reduced portability.
⸻
31. What is batch processing in Hibernate?
Batch processing groups multiple SQL statements into batches to reduce round trips to the database. Hibernate supports JDBC batching for inserts and updates. Proper batch size configuration significantly improves performance for bulk operations.
⸻
32. Difference between flush() and commit().
flush() synchronizes the persistence context with the database but does not commit the transaction. commit() finalizes the transaction. Hibernate may automatically flush before queries to ensure consistency.
⸻
33. What is dirty checking?
Dirty checking is Hibernate’s mechanism for detecting changes in persistent entities. Hibernate compares the current state with the snapshot taken at load time and generates SQL only if changes are detected. This reduces unnecessary updates.
⸻
34. What is dynamic update?
Dynamic update ensures Hibernate generates SQL UPDATE statements only for modified columns. It improves performance but has a slight overhead for tracking changes. It is useful for wide tables with many columns.
⸻
35. Explain @NamedQuery and @NamedNativeQuery.
Named queries are predefined queries that improve readability and performance. They are validated at startup, reducing runtime errors. Native named queries allow database-specific SQL when needed.
⸻
36. What is @Formula?
@Formula allows mapping a calculated SQL expression to a read-only entity attribute. It is useful for derived fields like full name or computed totals but should be used carefully due to DB dependency.
⸻
37. What is @Where clause?
@Where adds a static SQL condition to entity queries. It is commonly used for soft deletes. However, it applies globally and may cause confusion if not documented clearly.
⸻
38. Explain soft delete in Hibernate.
Soft delete marks records as inactive instead of deleting them. This is implemented using a flag column and annotations like @Where and @SQLDelete. It preserves historical data and audit trails.
⸻
39. What is Hibernate Envers?
Hibernate Envers provides auditing capabilities by maintaining historical versions of entities. It is useful for compliance and tracking changes. Envers automatically creates audit tables and manages versioning.
⸻
40. How do you tune Hibernate performance in production?
Performance tuning includes proper fetching strategies, query optimization, indexing, batch processing, caching, and monitoring SQL logs. Hibernate should be profiled with real data volumes. Interviewers expect a holistic answer covering ORM, DB, and JVM tuning.
REST API Principles
1. Client–Server
Client and server are separate; client handles UI, server handles data and logic.
2. Stateless
Each request contains all required information; server does not store client state.
3. Uniform Interface
Use standard HTTP methods (GET, POST, PUT, DELETE) and resource-based URLs.
4. Resource-Based
APIs expose resources using nouns (e.g., /users, /orders).
5. Representation
Resources are transferred in formats like JSON or XML.
6. Cacheable
Responses can be cached to improve performance.
7. Layered System
Client interacts with server through layers like gateway, security, or load balancer.
⸻
1. How do you design a RESTful API in Java?
Designing a RESTful API starts with identifying resources and modeling them using nouns, not verbs. For example, /users, /orders/{id} instead of /getUser. I follow REST principles such as statelessness, proper HTTP methods (GET, POST, PUT, PATCH, DELETE), and meaningful HTTP status codes. In Java, Spring Boot is commonly used with @RestController and @RequestMapping. I also focus on API versioning, pagination, filtering, and consistent response structures. A well-designed API should be intuitive, scalable, and backward-compatible.
2. What annotations are used to write REST APIs in Spring Boot?
Key annotations include @RestController to define REST controllers, @RequestMapping or method-specific annotations like @GetMapping, @PostMapping, @PutMapping, and @DeleteMapping. @RequestBody is used to bind JSON payloads to Java objects, while @PathVariable and @RequestParam are used for URL parameters. @ResponseStatus helps customize HTTP status codes. These annotations simplify API development and reduce boilerplate code.
3. Difference between@Controller and@RestController?
@Controller is used in MVC applications and typically returns views like JSP or Thymeleaf templates. @RestController is a combination of @Controller and @ResponseBody, meaning it directly returns data (usually JSON or XML). In REST APIs, @RestController is preferred because it eliminates the need to annotate each method with @ResponseBody and aligns with API-centric development.
4. How do you handle request validation in Java APIs?
Request validation is handled using Bean Validation (JSR-380) annotations like @NotNull, @NotBlank, @Size, and @Email. These are applied to DTO classes. The controller uses @Valid or @Validated to trigger validation. For handling validation errors, I use @ControllerAdvice with @ExceptionHandler to return meaningful error responses. Validation ensures data integrity and prevents invalid data from reaching the business layer.
5. How do you handle exceptions globally in REST APIs?
Global exception handling is implemented using @ControllerAdvice or @RestControllerAdvice. Custom exceptions are created for business errors, such as ResourceNotFoundException. Each exception is mapped to an appropriate HTTP status code. This approach centralizes error handling, improves readability, and ensures consistent error responses across APIs, which is critical for client applications.
6. How do you return proper HTTP status codes from APIs?
Spring Boot allows returning status codes using ResponseEntity, @ResponseStatus, or by throwing exceptions mapped to status codes. For example, 200 OK for success, 201 CREATED for resource creation, 400 BAD REQUEST for validation errors, 404 NOT FOUND for missing resources, and 500 INTERNAL SERVER ERROR for unexpected issues. Using correct status codes improves API usability and client-side error handling.
7. What is the difference between PUT and PATCH?
PUT is used for full updates, meaning the entire resource representation is replaced. PATCH is used for partial updates, where only specific fields are modified. In real-world APIs, PATCH is preferred for performance and flexibility, especially when dealing with large objects. Interviewers often expect candidates to mention idempotency — PUT is idempotent, while PATCH may or may not be.
8. How do you implement pagination and sorting in Java APIs?
In Spring Boot with Spring Data JPA, pagination and sorting are implemented using Pageable and Sort. The controller accepts parameters like page, size, and sort. The repository returns a Page<T> object, which includes metadata like total elements and total pages. Pagination improves performance and avoids returning large datasets in a single response.
9. How do you secure REST APIs in Java?
REST APIs are commonly secured using Spring Security with JWT or OAuth2. Authentication verifies user identity, while authorization controls access to resources. JWT is stateless and well-suited for microservices. APIs should also enforce HTTPS, validate input, and protect against common attacks like SQL injection and CSRF. Security is layered and should not rely on a single mechanism.
10. What is DTO and why is it used in APIs?
DTO (Data Transfer Object) is used to transfer data between client and server without exposing internal entity models. DTOs help control payload size, improve security, and decouple persistence logic from API contracts. In interviews, it’s important to say that entities should not be exposed directly in REST APIs.
11. Difference between synchronous and asynchronous APIs?
Synchronous APIs block the client until a response is returned, while asynchronous APIs return immediately and process requests in the background. In Java, async APIs can be implemented using @Async, CompletableFuture, or reactive programming. Async APIs improve scalability for long-running operations but add complexity.
12. How do you handle API versioning?
API versioning can be implemented using URI versioning (/api/v1/users), header versioning, or query parameters. URI versioning is the most common and easiest to understand. Versioning ensures backward compatibility and allows clients to migrate gradually without breaking changes.
13. How do you log API requests and responses?
Logging is handled using SLF4J with Logback or Log4j2. Filters or interceptors are used to log incoming requests and outgoing responses. Sensitive data like passwords or tokens should never be logged. Proper logging helps in debugging, monitoring, and auditing API behavior in production.
14. What is idempotency and why is it important?
Idempotency means that multiple identical requests have the same effect as a single request. HTTP methods like GET, PUT, and DELETE are idempotent, while POST is not. Idempotency is important in distributed systems to handle retries safely without creating duplicate resources.
15. How do you handle large file uploads in APIs?
Large file uploads are handled using multipart requests. Spring Boot supports this using MultipartFile. Configuration is required to set max file size and request size. For very large files, streaming or direct upload to object storage like S3 is preferred to avoid memory issues.
16. How do you document Java APIs?
APIs are documented using OpenAPI/Swagger. Springdoc OpenAPI or Swagger annotations are used to generate interactive API documentation. Good documentation improves developer experience and reduces integration errors.
17. Difference between REST and SOAP APIs?
REST is lightweight, uses HTTP methods, and typically exchanges JSON, making it suitable for modern web and mobile applications. SOAP is XML-based, heavier, and supports advanced standards like WS-Security. Most modern Java applications prefer REST due to simplicity and performance.
18. How do you handle concurrency in REST APIs?
Concurrency is handled using stateless services, database transactions, and optimistic locking. APIs should not store request-specific state in memory. For concurrent updates, versioning or locking mechanisms prevent data inconsistency.
19. How do you test Java REST APIs?
REST APIs are tested using unit tests with MockMvc, integration tests with Testcontainers or H2, and API tests using tools like Postman. Automated testing ensures reliability and prevents regressions.
20. What are common mistakes while writing Java APIs?
Common mistakes include exposing entities directly, using incorrect HTTP methods, poor error handling, returning wrong status codes, over-fetching data, ignoring validation, and tight coupling between layers. Avoiding these mistakes leads to clean, scalable, and maintainable APIs.
1. What is Functional Programming in Java?
Functional programming in Java is a programming style introduced mainly in Java 8 that emphasizes immutability, declarative code, and functions as first-class citizens. It allows behavior to be passed as parameters using lambda expressions and functional interfaces. Java’s functional programming is not purely functional but supports it through Streams, lambdas, and functional interfaces like Function, Predicate, and Consumer.
2. What is a Stream in Java?
A Stream represents a sequence of elements supporting functional-style operations like map, filter, and reduce. Streams do not store data; they operate on data from collections, arrays, or I/O channels. Streams enable clean, readable, and parallelizable code while abstracting iteration logic.
3. Difference between Collection and Stream?
Collection
Stores data, Eager evaluation, Can be reused, Imperative style
Stream
Does not store data, Lazy evaluation, Cannot be reused, Declarative style
⸻
4. What are intermediate and terminal operations?
Intermediate operations (like map, filter, sorted) return a Stream and are lazily evaluated. Terminal operations (like forEach, collect, reduce) trigger the stream processing and produce a result. Without a terminal operation, the stream does nothing.
⸻
5. Explain map() vs flatMap().
Interview Answer:
map() transforms each element into another element, resulting in a one-to-one mapping.
flatMap() flattens nested structures into a single stream. It is commonly used when working with collections inside collections.
⸻
6. What is lazy evaluation in Streams?
Lazy evaluation means stream operations are executed only when a terminal operation is invoked. This improves performance by avoiding unnecessary computations and enabling optimizations like short-circuiting.
⸻
7. What are Functional Interfaces?
A functional interface contains exactly one abstract method. It enables lambda expressions. Examples include Runnable, Callable, and interfaces in java.util.function.
⸻
8. What is the @FunctionalInterface annotation?
It ensures that the interface contains only one abstract method. It is optional but helps catch errors at compile time and improves readability.
⸻
9. Explain Function<T, R>.
Interview Answer:
Function<T, R> takes one input and returns one output. It is commonly used in map() operations to transform data.
10. Explain Consumer<T>.
Consumer<T> accepts one input and returns nothing. It is commonly used in forEach().
⸻
11. Explain Supplier<T>.
Supplier<T> takes no input and returns a value. It is used when data generation is required.
⸻
13. What is reduce()?
reduce() combines stream elements into a single value using an associative function. It is often used for sum, max, min, or custom aggregation.
⸻
14. What is a Method Reference?
Method references provide a shorthand for lambda expressions when an existing method matches the required functional interface. They improve readability and reduce boilerplate.
⸻
15. Types of Method References?
Interview Answer:
1. Static method reference — ClassName::methodName
2. Instance method reference — object::methodName
3. Instance method of arbitrary object — ClassName::methodName
4. Constructor reference — ClassName::new
⸻
16. Difference between Lambda Expression and Method Reference?
Lambdas define inline behavior, while method references reuse existing methods. Method references are more readable but less flexible than lambdas.
⸻
17. What is Optional and why is it used?
Optional is a container object that may or may not contain a value. It helps avoid NullPointerException by enforcing explicit null handling.
⸻
18. What are Parallel Streams?
Parallel streams divide tasks across multiple threads using the Fork/Join framework. They can improve performance for large datasets but must be used carefully due to thread-safety and overhead concerns.
⸻
19. When should you avoid Streams?
Streams should be avoided when logic is very simple, performance is critical for small loops, debugging is required, or when heavy side effects are involved.
⸻
20. Functional Programming Best Practices in Java
Best practices include avoiding side effects, using immutable data, preferring method references for readability, keeping lambdas short, and choosing sequential streams unless parallelism is truly beneficial.
⸻
1. What problem does Spring Boot solve?
Spring Boot eliminates boilerplate configuration in Spring applications. It provides auto-configuration, embedded servers, opinionated defaults, and production-ready features. This allows teams to focus on business logic rather than framework setup.
⸻
2. How does Spring Boot auto-configuration work internally?
Spring Boot uses @EnableAutoConfiguration which loads configurations listed in spring.factories. Based on classpath, properties, and conditions (@ConditionalOnClass, @ConditionalOnProperty), it configures beans automatically.
⸻
3. Difference between Spring and Spring Boot?
Spring is a framework, while Spring Boot is an opinionated extension. Spring Boot provides embedded servers, auto-configuration, and starter dependencies, significantly reducing setup time.
⸻
4. What are Spring Boot Starters?
Starters are dependency descriptors that bundle compatible dependencies. Example: spring-boot-starter-web includes Tomcat, Jackson, and Spring MVC, ensuring version compatibility.
⸻
5. What is embedded server and why is it important?
Spring Boot embeds servers like Tomcat or Jetty, allowing applications to run as standalone JARs. This simplifies deployment, supports microservices, and aligns well with containerization.
⸻
6. How does Spring Boot handle external configuration?
Spring Boot supports properties files, YAML, environment variables, command-line arguments, and config servers. It follows a well-defined property precedence order.
⸻
7. Difference between application.properties and application.yml?
YAML is more readable and hierarchical, while properties are flat key-value pairs. YAML is preferred for complex configurations.
⸻
8. What is Spring Boot Actuator?
Actuator provides production-ready endpoints like /health, /metrics, and /env for monitoring, management, and observability.
⸻
9. How do you secure Actuator endpoints?
Using Spring Security with role-based access and limiting exposure via management.endpoints.web.exposure.include.
⸻
10. What is the role of @SpringBootApplication?
It combines @Configuration, @EnableAutoConfiguration, and @ComponentScan, serving as the main entry point of the application.
⸻
11. How do profiles work in Spring Boot?
Profiles allow environment-specific configurations. They can be activated via properties, environment variables, or command-line arguments.
⸻
12. How do you override auto-configured beans?
By defining a custom bean of the same type or using @Primary. Spring Boot backs off when it finds user-defined beans.
⸻
13. What is CommandLineRunner?
It allows execution of code after application startup. Commonly used for initialization or data loading.
⸻
14. How does Spring Boot support microservices?
Through embedded servers, REST support, cloud integrations, configuration management, and observability tooling.
⸻
15. How do you implement REST APIs in Spring Boot?
Using @RestController, request mapping annotations, DTOs, validation, and proper HTTP status codes.
⸻
16. Difference between @Controller and @RestController?
@RestController returns JSON/XML directly, while @Controller returns views.
⸻
17. How do you handle exception handling globally?
Using @ControllerAdvice and @ExceptionHandler to provide consistent error responses.
⸻
18. How do you validate request payloads?
Using Bean Validation annotations with @Valid and handling errors centrally.
⸻
19. What is DTO and why is it important?
DTOs prevent exposing entities directly, reduce payload size, and decouple persistence from API contracts.
⸻
20. How does Spring Boot integrate with JPA/Hibernate?
Spring Boot auto-configures EntityManager, DataSource, transaction management, and Hibernate properties.
⸻
21. How do you prevent the N+1 problem?
Using fetch joins, entity graphs, batch fetching, and DTO projections.
⸻
22. Difference between @Transactional propagation types?
Propagation defines transaction behavior. REQUIRED joins or creates a transaction, while REQUIRES_NEW always starts a new one.
⸻
23. What is optimistic vs pessimistic locking?
Optimistic locking uses versioning without DB locks; pessimistic locking uses DB locks. Optimistic is preferred for scalability.
⸻
24. How do you implement pagination and sorting?
Using Pageable and Sort in Spring Data JPA repositories.
⸻
25. How do you secure APIs in Spring Boot?
Using Spring Security with JWT/OAuth2, method-level security, and HTTPS.
⸻
26. What is CSRF and how do you handle it?
CSRF is a cross-site request attack. It’s disabled for stateless REST APIs using JWT.
⸻
27. How does Spring Boot support async processing?
Using @Async, CompletableFuture, or reactive programming.
⸻
28. What is reactive programming in Spring Boot?
Reactive programming uses non-blocking streams via Spring WebFlux for high-throughput systems.
⸻
29. Difference between Spring MVC and WebFlux?
Spring MVC is blocking and servlet-based, while WebFlux is non-blocking and reactive.
⸻
30. How do you log in Spring Boot?
Using SLF4J with Logback or Log4j2, configured via logback.xml.
⸻
31. How do you monitor Spring Boot applications?
Using Actuator, Prometheus, Grafana, and centralized logging.
⸻
32. How do you handle configuration in microservices?
Using externalized config, environment variables, and centralized config servers.
⸻
33. What is API versioning and how do you do it?
Using URI versioning (/api/v1), headers, or query params.
⸻
34. How do you implement caching?
Using Spring Cache abstraction with annotations like @Cacheable.
⸻
35. How do you test Spring Boot applications?
Using unit tests, @WebMvcTest, @SpringBootTest, Testcontainers, and MockMvc.
⸻
36. Difference between unit and integration tests?
Unit tests test isolated components, integration tests verify interactions between components.
⸻
37. How do you deploy Spring Boot applications?
As JARs, Docker containers, or Kubernetes deployments.
⸻
38. How do you handle graceful shutdown?
Using actuator shutdown and container-aware lifecycle management.
⸻
39. What is Spring Boot DevTools?
Provides auto-restart and live reload for faster development.
⸻
40. How do you tune Spring Boot performance?
By optimizing queries, connection pools, caching, JVM tuning, and thread management.
⸻
41. How do you manage database migrations?
Using Flyway or Liquibase.
⸻
42. What is OpenAPI/Swagger?
It documents REST APIs and provides interactive UI for testing.
⸻
43. How do you handle large file uploads?
Using multipart uploads or streaming to object storage.
⸻
44. How do you avoid tight coupling?
Using interfaces, dependency injection, and layered architecture.
⸻
45. How do you handle backward compatibility?
Using versioned APIs and non-breaking changes.
⸻
46. How do you design fault-tolerant APIs?
Using retries, timeouts, circuit breakers, and graceful degradation.
⸻
47. What is circuit breaker pattern?
It prevents cascading failures by stopping calls to failing services.
⸻
48. How do you handle distributed transactions?
Using eventual consistency and Saga patterns.
⸻
49. What are common Spring Boot anti-patterns?
Using EAGER fetch everywhere, exposing entities, OSIV misuse, and overusing annotations.
⸻
50. What makes a Spring Boot application production-ready?
Proper configuration, security, monitoring, logging, testing, scalability, and observability.
Scenario: Your Spring Boot API is slow in production. How will you troubleshoot it?
First, I identify whether the issue is at the application, database, or infrastructure level. I enable Actuator metrics to analyze response times, thread usage, and memory. I check logs for slow queries and enable Hibernate SQL logging temporarily. Then I use APM tools like New Relic or Prometheus to identify bottlenecks. Common fixes include query optimization, adding indexes, fixing N+1 issues, enabling caching, and tuning connection pools. I always test changes with production-like data before rollout.
Scenario: You see an N+1 query problem in production. How do you fix it?
I confirm the issue by checking SQL logs. Then I decide the fix based on use case:
• Use fetch join or EntityGraph for read-heavy APIs
• Use DTO projections for REST APIs
• Apply batch fetching when pagination is required
I avoid switching everything to EAGER fetch because it causes performance issues. The solution must be scoped and use-case specific.
]Scenario: One microservice is down and others are failing. What will you do?
This is a cascading failure scenario. I implement timeouts, retries, and circuit breakers using Resilience4j. I also ensure fallback responses where possible. At architecture level, I make sure services are loosely coupled and communicate asynchronously where possible. In production, I monitor failure rates and open circuits quickly to protect the system.
⸻
Scenario: You need different configurations for dev, QA, and prod. How do you manage it?
I use Spring Profiles with separate config files like application-dev.yml, application-qa.yml, and application-prod.yml. Sensitive values are injected using environment variables or secret managers. Profiles are activated via environment or command-line arguments. This keeps configuration externalized and secure.
⸻
Scenario: Your REST API is exposing too much data. How do you fix it?
I never expose entities directly. I introduce DTOs and map only required fields. I also apply JSON annotations like @JsonIgnore where needed. For performance-critical APIs, I switch to DTO projections directly from the database to reduce memory and serialization overhead.
⸻
Scenario: A client complains about duplicate data when retrying requests.
This is an idempotency issue. I ensure that GET, PUT, and DELETE operations are idempotent. For POST requests, I introduce an idempotency key so retries do not create duplicate records. This is especially important in distributed systems and payment APIs.
⸻
Scenario: You need to support backward compatibility for existing clients.
I use API versioning, usually URI-based (/api/v1, /api/v2). I avoid breaking changes and introduce new fields in a backward-compatible way. Old versions are deprecated gradually with proper communication. This avoids sudden client failures.
⸻
Scenario: Database schema changes need to be deployed safely.
I use Flyway or Liquibase for database migrations. Every schema change is version-controlled and backward-compatible. Destructive changes are avoided or done in phases. Migrations are tested in lower environments before production rollout.
⸻
Scenario: Your application consumes too much memory under load.
I analyze heap usage using JVM metrics and memory dumps. Common causes are large result sets, EAGER fetching, or memory leaks. I introduce pagination, streaming responses, and caching where appropriate. I also tune JVM heap size and garbage collection based on workload.
⸻
Scenario: Multiple users update the same record simultaneously.
I use optimistic locking with a @Version column. If conflicts occur, Hibernate throws an exception which I handle gracefully. Pessimistic locking is used only when absolutely necessary, as it impacts scalability.
⸻
Scenario: You need to execute a long-running task without blocking API response.
I make the API asynchronous using @Async, messaging (Kafka/RabbitMQ), or background job processing. The API responds immediately with a tracking ID, and the client polls or listens for completion events.
⸻
Scenario: Your API needs to handle high traffic spikes.
I design stateless services, enable horizontal scaling, use load balancers, caching, and rate limiting. I also ensure database connection pool tuning and thread pool sizing are optimized.
⸻
Scenario: Logging is flooding disk space in production.
I adjust log levels, enable log rotation, and centralize logs using ELK or similar tools. Sensitive data is masked. Debug logs are never enabled in production permanently.
⸻
Scenario: You want to monitor application health in production.
Interview Answer:
I use Spring Boot Actuator with custom health indicators. Metrics are exported to Prometheus and visualized in Grafana. Alerts are configured for threshold breaches.
⸻
Scenario: REST API response time increases as data grows.
I introduce pagination, proper indexing, caching, and query optimization. I also avoid fetching unnecessary relationships. For heavy read APIs, I consider read replicas or CQRS patterns.
Microservices — 12-Factor Principles
The 12-Factor App is a methodology for building cloud-native, scalable microservices, originally defined by Heroku.
The 12-Factor methodology defines best practices for building scalable, cloud-native microservices using stateless processes, externalized configuration, and automated deployments.
⸻
Codebase
One codebase per service, tracked in version control.
Each microservice has its own repository and lifecycle.
⸻
Dependencies
All dependencies are explicitly declared and isolated.
Example: Maven/Gradle manages dependencies — no shared libraries on the server.
⸻
Config
Configuration is stored in environment variables, not in code.
Examples: DB URL, credentials, API keys.
- Enables easy deployment across environments.
⸻
Backing Services
Treat databases, message brokers, caches as attached resources.
They can be swapped without code changes.
Example: MySQL → PostgreSQL without code rewrite.
⸻
Build, Release, Run
Strict separation of stages:
• Build → compile/package
• Release → config + artifact
• Run → execution
✔ Improves deployment reliability.
⸻
Processes
Apps run as stateless processes.
No session data or state stored in memory.
✔ Enables horizontal scaling.
⸻
Port Binding
Services are self-contained and expose functionality via ports.
Example: Spring Boot runs on embedded Tomcat.
✔ No external app server dependency.
⸻
Concurrency
Scale by adding more service instances, not by increasing threads.
Horizontal scaling is preferred.
⸻
Disposability
Services should start fast and shut down gracefully.
Supports rolling deployments and crash recovery.
⸻
Dev/Prod Parity
Keep development, QA, and production environments similar.
Avoid “works on my machine” issues.
⸻
Logs
Logs are treated as event streams, not files.
Sent to centralized logging systems (ELK, Splunk).
⸻
Admin Processes
Admin or maintenance tasks run as one-off processes.
Example: DB migrations, batch jobs.
Synchronization ensures data consistency through blocking, while asynchronization improves scalability through non-blocking execution. Hashtable is a legacy synchronized map, HashMap is fast but not thread-safe, and ConcurrentHashMap provides thread safety with high performance using fine-grained locking.
Synchronization
Synchronization is a mechanism used to control access to shared resources so that only one thread can execute a critical section at a time. It ensures thread safety and data consistency by preventing race conditions. However, synchronization introduces blocking, meaning threads may have to wait, which can reduce performance and scalability. In Java, synchronization is often associated with intrinsic locks and is commonly used when data consistency is more critical than performance.
Key points interviewers expect:
• Ensures mutual exclusion
• Prevents race conditions
• Causes thread blocking
• Can lead to performance bottlenecks if overused
⸻
Asynchronization
Asynchronization allows tasks to execute independently without blocking the calling thread. Instead of waiting for a task to complete, the system continues processing other work. Asynchronous execution improves responsiveness, throughput, and scalability, especially in I/O-bound and distributed systems. However, it introduces complexity in error handling, coordination, and debugging. In modern Java systems, asynchronous processing is preferred for high-performance and scalable applications.
Key points interviewers expect:
• Non-blocking execution
• Better resource utilization
• Improves scalability and responsiveness
• Adds complexity in flow control
Hashtable vs HashMap vs ConcurrentHashMap
Hashtable
Hashtable is a legacy collection that is thread-safe by default because all its methods are synchronized. While this guarantees data consistency, it results in poor performance under high concurrency due to excessive locking. Hashtable does not allow null keys or values and is generally avoided in modern applications in favor of more efficient alternatives.
Key points interviewers expect:
• Thread-safe (method-level synchronization)
• Poor scalability
• Legacy class
• No null key or value
⸻
HashMap
HashMap is not thread-safe and is designed for single-threaded or externally synchronized environments. It offers better performance than Hashtable because it does not use synchronization. HashMap allows one null key and multiple null values. In concurrent environments, using HashMap without proper synchronization can lead to data inconsistency or infinite loops.
Key points interviewers expect:
• Not thread-safe
• High performance
• Allows one null key
• Requires external synchronization in multithreaded use
⸻
ConcurrentHashMap
ConcurrentHashMap is a high-performance, thread-safe collection designed for concurrent access. Unlike Hashtable, it uses fine-grained locking and non-blocking techniques, allowing multiple threads to read and update the map simultaneously. It provides better scalability and throughput in multi-threaded applications. ConcurrentHashMap does not allow null keys or values to avoid ambiguity in concurrent operations.
Key points interviewers expect:
• Thread-safe with high concurrency
• Fine-grained locking
• Better performance than Hashtable
• No null keys or values
⸻
Richardson Maturity Model (RMM)
The Richardson Maturity Model is a way to measure how RESTful an API is. It defines four levels (0–3) that represent increasing compliance with REST principles.
Levels:
• Level 0 — RPC Style
Single endpoint, HTTP used as a transport only.
• Level 1 — Resources
Multiple resource-based URLs, but limited HTTP method usage.
• Level 2 — HTTP Verbs
Proper use of HTTP methods (GET, POST, PUT, DELETE) and status codes.
• Level 3 — HATEOAS
Responses contain links that guide client interactions dynamically.
Interview Insight:
Most real-world REST APIs operate at Level 2. Level 3 is rarely fully implemented.
⸻
NFR — Non-Functional Requirements
Non-Functional Requirements define how a system behaves, not what it does. They focus on quality attributes such as performance, security, scalability, and reliability. NFRs are critical because they directly impact user experience and system stability, especially in production.
Common NFR Categories:
• Performance — response time, throughput
• Scalability — ability to handle growth
• Availability — uptime guarantees
• Security — authentication, authorization
• Reliability — fault tolerance
• Maintainability — ease of change
• Observability — logging, monitoring
Interview Insight:
Functional requirements make the system work; NFRs make the system usable in production.
⸻
REST Theory
REST (Representational State Transfer) is an architectural style for building scalable and maintainable web services. It is based on stateless client-server communication and resource-based interactions using standard HTTP semantics.
Core REST Principles:
• Client–Server separation
• Statelessness
• Uniform interface
• Resource-based URLs
• Representation-driven responses (JSON/XML)
• Cacheability
• Layered system
REST is not about JSON or HTTP — it’s about scalability, simplicity, and loose coupling.
⸻
SOLID Principles
SOLID is a set of object-oriented design principles that help create maintainable, scalable, and flexible software systems. These principles reduce tight coupling and improve code readability and testability.
SOLID Breakdown:
• S — Single Responsibility Principle
A class should have only one reason to change.
• O — Open/Closed Principle
Software entities should be open for extension, closed for modification.
• L — Liskov Substitution Principle
Subtypes must be replaceable without breaking behavior.
• I — Interface Segregation Principle
Clients should not depend on unused interfaces.
• D — Dependency Inversion Principle
Depend on abstractions, not concrete implementations.
Interview Insight:
SOLID principles help manage complexity as systems scale.
⸻
• RMM: Measures how RESTful an API is, from basic RPC to HATEOAS.
• NFR: Defines system quality attributes like performance and security.
• REST: Stateless, resource-based architectural style using HTTP semantics.
• SOLID: Design principles for building maintainable object-oriented systems.
⸻
Question: Explain Java 8 Streams and how they differ from collections.
Java 8 Streams represent a functional, declarative approach to data processing. Unlike collections, streams do not store data; instead, they operate on data sources such as collections, arrays, or I/O channels. Streams enable developers to express complex operations like filtering, mapping, and aggregation in a concise and readable way using lambda expressions. One major difference is that streams support lazy evaluation, meaning operations are executed only when a terminal operation is invoked. This improves performance, especially for large datasets. Streams can also be processed sequentially or in parallel, allowing better CPU utilization. In real-world applications, streams help reduce boilerplate code and improve maintainability, but improper use of parallel streams may lead to performance issues due to thread overhead.
What is CI/CD?
CI/CD stands for Continuous Integration and Continuous Delivery/Deployment. It is an automated process that enables teams to build, test, and deploy applications frequently and reliably. CI/CD reduces manual intervention, improves software quality, and accelerates delivery.
⸻
Continuous Integration (CI)
What it means
Continuous Integration ensures that every code change pushed to the repository is automatically built and tested.
GitHub-based CI workflow (high level):
1. Developer pushes code to GitHub (feature branch or PR)
2. CI pipeline triggers automatically
3. Code is compiled
4. Unit tests and static analysis run
5. Build artifact is generated
Interview Key Point
CI helps detect bugs early and prevents “integration hell”.
⸻
Continuous Delivery vs Continuous Deployment
Continuous Delivery
• Code is always in a deployable state
• Deployment to production is manual approval based
Continuous Deployment
• Every successful build is automatically deployed to production
Most enterprises use Continuous Delivery, not full Continuous Deployment.
⸻
GitHub-Based CI/CD Pipeline (Working Knowledge)
Typical Pipeline Stages
Source
• Code hosted in GitHub
• Triggers: push, pull request, merge to main branch
Build
• Maven/Gradle build
• Dependency resolution
• Artifact creation (JAR, Docker image)
Test
• Unit tests
• Integration tests
• Code quality checks (Sonar, Checkstyle)
Security & Quality Gates
• Static code analysis
• Vulnerability scanning
• Test coverage checks
Package
• Build Docker image or versioned artifact
• Push to artifact repository (Docker registry, Nexus)
Deploy
• Deploy to Dev / QA / Prod
• Kubernetes, VM, or cloud service
• Manual approval for prod (usually)
⸻
Branching Strategy in GitHub CI/CD
Common approach:
• main / master → production
• develop → integration
• feature/* → development
• release/* → stabilization
CI runs on:
• Pull requests
• Merge to main branches
⸻
CI/CD Processes
1. Code commit to GitHub
2. Automated pipeline triggered
3. Build + tests executed
4. Quality gates evaluated
5. Artifact versioned and stored
6. Deployment to environment
7. Monitoring and rollback if needed
⸻
Benefits of CI/CD
Technical Benefits
✔ Early bug detection
✔ Automated testing
✔ Consistent deployments
✔ Reduced human error
✔ Faster feedback loop
Business Benefits
✔ Faster time-to-market
✔ Higher release confidence
✔ Improved customer satisfaction
✔ Better team productivity
⸻
CI/CD Best Practices
✔ Keep pipelines fast
✔ Fail fast on errors
✔ Automate tests aggressively
✔ Use immutable artifacts
✔ Separate build and deploy stages
✔ Secure secrets using environment variables
✔ Enable rollback strategies
⸻
Common CI/CD Challenges (Interviewers Like This)
• Flaky tests breaking pipelines
• Long build times
• Environment differences
• Poor rollback strategies
• Over-manual approvals
⸻
CI/CD is an automated process that continuously builds, tests, and deploys applications using GitHub pipelines, improving software quality, reducing deployment risk, and accelerating delivery.
⸻
In GitHub-based CI/CD, every code change triggers automated build and test pipelines, producing versioned artifacts that are deployed consistently across environments. CI/CD improves reliability, scalability, and release velocity while reducing manual errors.










