Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
UML (Unified Modeling Language) diagrams help visualize system design. In LLD interviews, youβll mainly use Class Diagrams and Sequence Diagrams. Think of UML as the architectural drawings of software. Just as a building architect sketches floor plans before laying bricks, you sketch class diagrams before writing code. The goal is not to create museum-quality diagrams β it is to communicate your design clearly and quickly. In a 45-minute interview, a well-drawn class diagram can replace 10 minutes of verbal explanation and immediately signal that you think structurally about code.Class Diagrams
Class diagrams show the static structure of a system.Basic Notation
Visibility Symbols
| Symbol | Meaning | Access |
|---|---|---|
+ | Public | Everywhere |
- | Private | Same class only |
# | Protected | Same class + subclasses |
~ | Package | Same package |
Relationships
Multiplicity
Complete Example: E-commerce System
Sequence Diagrams
Sequence diagrams show how objects interact over time.Basic Elements
Complete Example: Order Checkout
Alt/Opt/Loop Fragments
Use Case Diagram (Bonus)
For understanding system scope:Interview Tips
Start Simple, Then Layer In Detail
Start Simple, Then Layer In Detail
Talk While Drawing
Talk While Drawing
Use Proper Notation
Use Proper Notation
Focus on Important Parts
Focus on Important Parts
Multiplicity Matters
Multiplicity Matters
Interview Questions
You are in an LLD interview designing a Hotel Booking system. The interviewer asks you to draw the relationship between Hotel, Room, and Guest. Walk me through exactly which UML relationships you would use and why.
You are in an LLD interview designing a Hotel Booking system. The interviewer asks you to draw the relationship between Hotel, Room, and Guest. Walk me through exactly which UML relationships you would use and why.
- Hotel to Room is composition (filled diamond on the Hotel side). A Room cannot exist independently of a Hotel. If the Hotel is destroyed (deleted from the system), its Rooms have no meaning. The Hotel controls the Room lifecycle. Multiplicity is
1on Hotel,1..*on Room (a hotel must have at least one room). - Hotel to Guest is association (simple line). A Guest interacts with a Hotel but has an independent lifecycle. The Guest existed before booking and continues to exist after checkout. Multiplicity is
0..*on both sides (a hotel has many guests over time, a guest can visit many hotels). - Guest to Room is NOT a direct relationship. Instead, there is a Booking (or Reservation) class that connects them. Booking to Guest is association (
1Guest per booking, a Guest has0..*bookings). Booking to Room is also association (1..*Rooms per booking, a Room has0..*bookings over time). - The Booking class is critical because it carries its own data: check-in date, check-out date, status, payment reference. Without it, you cannot model the temporal aspect (a room is available some dates but not others). This is the βassociation classβ pattern in UML β when the relationship itself has attributes.
- The interviewer says βwhat if a Room can be transferred to a different Hotel during a renovation?β Does this change your relationship from composition to aggregation, and what are the implications for your code?
- How would you represent a RoomType (Standard, Deluxe, Suite) in the diagram β as an enum attribute on Room, a separate class, or an inheritance hierarchy?
Explain the difference between a dependency (dashed arrow) and an association (solid line) in UML. When would you use each in a class diagram?
Explain the difference between a dependency (dashed arrow) and an association (solid line) in UML. When would you use each in a class diagram?
- An association means one class knows about another and holds a reference to it, typically as an instance variable. The relationship is long-lived β the reference persists for the lifetime of the object. For example, an Order has a
customer: Customerfield. The Order always knows which Customer placed it. - A dependency means one class uses another temporarily, usually as a method parameter, local variable, or return type. The relationship is transient β it exists only during a method call. For example, an Orderβs
generateInvoice()method might take anInvoicePrinteras a parameter. Order does not hold a reference to InvoicePrinter; it only uses it momentarily. - The practical importance: associations create tighter coupling than dependencies. If Customer changes its interface, Order is affected because it holds a persistent reference. If InvoicePrinter changes, Order is only affected if
generateInvoice()is called. This distinction matters for understanding change propagation and testability. - In your diagram, use solid lines (association) for βthis class has a field of that type.β Use dashed arrows (dependency) for βthis class receives that type as a parameter or creates it locally.β Getting this right shows you understand coupling strength, not just βthings are connected.β
- In the e-commerce diagram, CheckoutService depends on PaymentProcessor but does not have an association. Why is that a deliberate design choice, and how does it relate to Dependency Inversion?
- Can a dependency become an association over time as requirements evolve? Give an example of when you would promote a dependency to an association.
You draw a sequence diagram for a checkout flow. The interviewer says: 'I see 7 synchronous calls chained together. What problem does this create, and how would you redesign it?'
You draw a sequence diagram for a checkout flow. The interviewer says: 'I see 7 synchronous calls chained together. What problem does this create, and how would you redesign it?'
- A chain of 7 synchronous calls creates a fragile pipeline. If any single call in the chain fails or is slow, the entire operation hangs or fails. The total latency is the sum of all 7 calls. The error handling is complex because a failure at step 5 might require rolling back steps 1 through 4. This is the βdistributed monolithβ anti-pattern hiding behind an object-oriented design.
- The first fix is to identify which calls MUST be synchronous (they depend on the previous result) and which can be async (they are notifications or side effects). In a checkout flow: validating inventory and processing payment must be synchronous (you need the result before proceeding). But sending confirmation email, updating analytics, and notifying the warehouse can be asynchronous.
- I would redraw the sequence diagram splitting it into two phases: a synchronous critical path (validate stock, reserve inventory, charge payment, confirm order β 4 calls) and an async fan-out (email, analytics, warehouse notification β 3 calls via a message queue or event bus). The async calls use the Observer pattern or pub/sub.
- On the diagram, I would show the async messages with double-lined arrows or annotate them with
<<async>>. I would also add analtfragment around the payment call to show the failure path: if payment fails, release inventory and return error. This makes the failure handling explicit in the diagram.
- In the async fan-out, what happens if the email service is down? The order is confirmed but the customer never gets the confirmation email. How do you handle this and how would it show on the diagram?
- How would you represent a retry mechanism with exponential backoff on the sequence diagram?
An interviewer gives you a UML class diagram and asks you to identify SOLID violations just by looking at it. What are the visual signals you search for?
An interviewer gives you a UML class diagram and asks you to identify SOLID violations just by looking at it. What are the visual signals you search for?
- For SRP violations: classes with many unrelated methods. If an Order class box has methods for
processPayment(),sendEmail(),generatePDF(), andupdateInventory()alongsidecalculateTotal(), that is visible multi-responsibility. Also watch for class names ending in βManager,β βHandler,β or βUtilsβ β they are magnets for unrelated behavior. - For OCP violations: missing abstraction layers. If CheckoutService has a direct association arrow to StripePayment (a concrete class) instead of to a PaymentGateway interface, adding PayPal requires modifying CheckoutService. The visual signal is solid arrows pointing to concrete classes rather than to interfaces (boxes with
<<interface>>stereotype). - For LSP violations: inheritance arrows where the child class likely cannot honor the parentβs contract. If ReadOnlyFile inherits from File which has
write()in its method list, that is a visible LSP smell. Also watch for deep hierarchies (4+ levels) β the deeper the hierarchy, the more likely LSP violations lurk. - For ISP violations: interfaces with many methods where some implementors stub them out. If an
<<interface>> IWorkerhas 5 methods and Robot implements it, and you know robots cannot eat or sleep, those empty implementations are ISP violations. The visual signal is a fat interface box with many methods. - For DIP violations: high-level classes with association arrows pointing directly to low-level concrete classes. If OrderService points to MySQLDatabase rather than to a Database interface, that is DIP on the diagram.
- Can you always detect design problems from a class diagram alone? What kinds of problems require a sequence diagram or code inspection to find?
- If you see a class implementing 6 interfaces, is that a design smell or a sign of good ISP compliance? How do you tell the difference?
What is the difference between the `<<include>>` and `<<extend>>` relationships in a Use Case Diagram, and why do candidates consistently get this wrong?
What is the difference between the `<<include>>` and `<<extend>>` relationships in a Use Case Diagram, and why do candidates consistently get this wrong?
<<include>>means the base use case ALWAYS incorporates the included use case. It is mandatory. βCheckoutβ always includes βValidate Payment.β Every time checkout runs, payment validation runs. Think of it as a function call that always happens.<<extend>>means the extending use case SOMETIMES augments the base use case, under certain conditions. βApply Couponβ extends βCheckoutβ β it only happens if the user has a coupon. It is conditional and optional. The base use case is complete without it.- Candidates get this wrong because the arrows point in counterintuitive directions. In
<<include>>, the arrow goes FROM the base use case TO the included one (Checkout βincludeβ> Validate Payment). In<<extend>>, the arrow goes FROM the extension TO the base (Apply Coupon βextendβ> Checkout). The extension βoffers itselfβ to the base, which is backwards from how most people think about optional behavior. - The practical test: if removing the relationship would make the base use case incomplete or broken, it is
<<include>>. If removing it would just mean a feature is missing but the base still works, it is<<extend>>.
- In practice, how useful are Use Case Diagrams in LLD interviews compared to Class and Sequence Diagrams? When would you proactively draw one versus waiting for the interviewer to ask?
- Can you have an
<<extend>>that extends another<<extend>>? If so, give an example. If not, why not?
You are in an interview and the interviewer says: 'You have 5 minutes to draw the class diagram for a Parking Lot system. Go.' Walk me through your approach -- not the final diagram, but HOW you decide what to draw.
You are in an interview and the interviewer says: 'You have 5 minutes to draw the class diagram for a Parking Lot system. Go.' Walk me through your approach -- not the final diagram, but HOW you decide what to draw.
- First 60 seconds: identify the core nouns from the problem statement. Parking Lot, Floor, Parking Spot, Vehicle, Ticket, and Payment. These become my class candidates. I write them as boxes on the board without any details β just names.
- Next 60 seconds: draw the relationships. ParkingLot to Floor is composition (floors do not exist without the lot), Floor to ParkingSpot is composition (spots do not exist without the floor), ParkingSpot to Vehicle is association (a spot may or may not have a vehicle), Ticket ties together a Vehicle and a ParkingSpot with timestamps. I add multiplicity:
1ParkingLot,1..*Floors,nParkingSpotsPerFloor,0..1Vehicle per spot. - Next 60 seconds: add the key attributes and methods that reveal design decisions. ParkingSpot gets a
type(compact, regular, large) and anisAvailable()method. Vehicle gets atype(motorcycle, car, bus) that determines which spot it can use. Ticket getsentryTimeandexitTimefor billing. - Next 60 seconds: add the abstraction layer. Vehicle becomes an abstract class or interface with Motorcycle, Car, Bus subclasses. ParkingSpot might also be abstract with CompactSpot, RegularSpot, LargeSpot subclasses, or simply use a type enum β I would ask the interviewer which they prefer and explain the trade-off.
- Final 60 seconds: add the Strategy pattern for payment (CashPayment, CardPayment) and a Singleton annotation on ParkingLot if appropriate. Review the diagram for any missing relationships.
- You drew ParkingSpot with subtypes CompactSpot, RegularSpot, LargeSpot. The interviewer says βa bus takes up 5 regular spots.β How does your diagram handle this, and does it change the relationship between Vehicle and ParkingSpot?
- How would you add an entry/exit gate system to the diagram? What relationship does it have with Ticket and ParkingLot?
In the e-commerce class diagram example, Customer to Order shows a filled diamond (composition) with multiplicity 1 to *. A colleague argues it should be aggregation because an Order is a meaningful business entity even after a Customer deletes their account. Who is right?
In the e-commerce class diagram example, Customer to Order shows a filled diamond (composition) with multiplicity 1 to *. A colleague argues it should be aggregation because an Order is a meaningful business entity even after a Customer deletes their account. Who is right?
- The colleague raises a legitimate point, and in most real e-commerce systems, they are right β it should be aggregation, not composition. Here is why: in a real business, an Order has legal and financial significance beyond the Customer. Tax records, audit trails, and fulfillment obligations require the Order to persist even if the Customer deletes their account. GDPR might require anonymizing the Customer data, but the Order itself (amounts, dates, items) must be retained for accounting.
- However, the βcorrectβ answer depends on the domain requirements. If your system is a simple MVP where deleting a customer cascades to their orders (no legal or financial obligations), composition is appropriate. If it is a production e-commerce platform with regulatory requirements, aggregation is correct.
- This is exactly the kind of design discussion interviewers want to hear. The best answer is not βcompositionβ or βaggregationβ β it is βhere is the question I would ask the interviewer to decide: what happens to orders when a customer is deleted?β This shows you understand that UML relationships encode business rules, not just structural preferences.
- In database terms, composition maps to
ON DELETE CASCADE. Aggregation maps toON DELETE SET NULLor a soft-delete pattern. The diagram choice directly affects your schema design, and getting it wrong in production means either orphaned records or illegally deleted financial records.
- If you change this to aggregation, how do you handle the display of βCustomerβs Order Historyβ when the Customer object still exists? Does the Order need a
customer_idfield, a direct reference, or both? - How does the composition vs aggregation choice affect your API design? If a client sends
DELETE /customers/123, what should happen to their orders in each case?
Multiplicity on UML diagrams: what is the practical difference between `0..*` and `1..*`, and can you give an example where getting this wrong caused a real bug?
Multiplicity on UML diagrams: what is the practical difference between `0..*` and `1..*`, and can you give an example where getting this wrong caused a real bug?
0..*means the collection can be empty.1..*means there must be at least one element at all times. The difference is an invariant β code that assumes at least one element will crash or behave incorrectly if the collection is empty.- Real example: Order to OrderItem. If you mark this as
0..*, your system allows an Order with zero items. Downstream code that callsorder.items[0]to get the first item for display, orsum(item.price for item in order.items)for the total, will either crash with an IndexError or silently return 0 β which might trigger a βfree orderβ bug. Marking it1..*means the Order class must enforce this invariant in its constructor oradd_item()method, and reject orders with no items. - Another real case: a User to Addresses with
1..*means the system requires at least one address on file. But during onboarding, the user has not entered an address yet. If your system insists on1..*from day one, the registration flow breaks. The correct multiplicity might be0..*on the User-Address association but1..*on the Order-ShippingAddress association (you need an address to ship, but not to create an account). - In code,
1..*should be enforced. In Python, the constructor checksif len(items) == 0: raise ValueError("Order must have at least one item"). In Java, you might use a non-empty collection type. The multiplicity on the diagram is a specification that drives validation logic.
* for everything, the code handles itβ (no precision, no invariant enforcement) or βIt is just documentation, it does not affect codeβ (misses the design-to-implementation link).Follow-ups:- How do you enforce
1..*in a database schema? What constraint would you add, and why is it harder to enforce than it sounds? - In the e-commerce diagram, Product has
stock: int. What multiplicity would you put on the association between Product and OrderItem, and what happens at the boundary when stock reaches zero?
Interview Deep-Dive
You draw a class diagram during an interview and the interviewer points to a relationship arrow and asks: 'Why is this composition and not aggregation?' How do you defend your choice?
You draw a class diagram during an interview and the interviewer points to a relationship arrow and asks: 'Why is this composition and not aggregation?' How do you defend your choice?
- The distinction is lifecycle ownership. Composition (filled diamond) means the contained objectβs lifecycle is entirely controlled by the container. If the container is destroyed, the contained objects are destroyed with it. Aggregation (open diamond) means the contained objects have independent lifecycles and can exist without the container.
- For example, in an e-commerce system: Order and OrderItem are composition. If you delete an order, its line items have no meaning on their own. But Team and Player are aggregation β if a team is dissolved, the players still exist and can join other teams.
- In my diagram, I chose composition here because [specific to the relationship] the contained object has no identity or purpose outside the container. It cannot be shared across multiple containers, and creating it independently would be a design error.
- A practical test I use: βCan I meaningfully create this object without the container existing first?β If the answer is no, it is composition. If yes, it is aggregation.
When would you use a sequence diagram instead of a class diagram in an LLD interview, and what are the key elements you would include?
When would you use a sequence diagram instead of a class diagram in an LLD interview, and what are the key elements you would include?
- I use a sequence diagram when the interviewer asks about a specific interaction flow, like βwalk me through the checkout processβ or βwhat happens when a user books a seat.β Sequence diagrams show temporal ordering and message passing between objects, which class diagrams cannot express.
- The key elements I always include: lifelines (vertical dashed lines) for each participating object, synchronous call arrows (solid lines with filled arrowheads) for method invocations, return arrows (dashed lines) for responses, and activation boxes (rectangles on the lifeline) to show when an object is actively processing.
- I also use alt/opt/loop fragments when relevant. Alt shows branching (payment succeeds vs. fails). Opt shows conditional behavior (apply coupon if present). Loop shows iteration (validate each item in cart).
- What I skip: I do not draw every getter call or logging statement. I focus on the meaningful interactions that show the designβs flow of control and the responsibilities of each object.
An interviewer gives you a UML class diagram for an e-commerce system and asks you to identify SOLID violations just by looking at the diagram. What do you look for?
An interviewer gives you a UML class diagram for an e-commerce system and asks you to identify SOLID violations just by looking at the diagram. What do you look for?
- For SRP violations, I look at class names ending in βManager,β βHandler,β or βUtils,β and at classes with many unrelated methods. If an Order class has methods for payment processing, email sending, and PDF generation alongside order management, that is a multi-responsibility class.
- For OCP violations, I look for missing abstractions. If the diagram shows a concrete PaymentProcessor with no interface above it and a direct dependency from CheckoutService, adding a new payment method will require modifying PaymentProcessor.
- For LSP violations, I look at inheritance hierarchies where a child class is likely to break the parentβs contract. If ReadOnlyFile inherits from File which has a write() method, that is a classic LSP violation visible on the diagram.
- For ISP violations, I look at interfaces with many methods and implementing classes that only use a subset. If an IWorker interface has work(), eat(), and sleep() methods but Robot implements IWorker, that fat interface is visible on the diagram.
- For DIP violations, I look at dependency arrows pointing from high-level classes directly to concrete low-level classes rather than to interfaces. If OrderService has an arrow directly to MySQLDatabase rather than to a Database interface, that is a DIP violation.
- Multiplicity also reveals design issues. If every relationship is β1 to *β with no upper bound, that might indicate unbounded collections that cause performance problems.