1. What is software architecture?
Software architecture is the high-level structure of a software system — the big-picture decisions that shape how the system is organized, how its components fit together, and how it achieves its non-functional goals. It is the blueprint: which components exist, what their responsibilities are, how they communicate, and what the constraints and trade-offs are.
Architecture addresses the qualities that matter at scale — performance, scalability, security, reliability, maintainability, and deployment. It is about the decisions that are hardest to change later, like the choice between a monolithic and a microservices structure, or whether the system uses a shared database. These decisions are usually made early, because redoing them after code exists is enormously expensive.
The architecture is captured in architectural views — a logical view of components, a deployment view of hardware and network, a process view of runtime behavior. Good architecture is deliberately simple, keeps options open where uncertainty exists, and lets the system grow without rework. It is the skeleton the rest of the project’s flesh hangs on.
2. What is the difference between software architecture and software design?
Architecture is the high-level structure; design is the detailed arrangement. Architecture concerns the system’s overall shape — components, connectors, deployment, and the non-functional qualities. Design concerns how individual components, classes, and modules are structured internally to implement that architecture.
Architecture answers “what are the major pieces and how do they interact?” It is concerned with system-level concerns: scalability, availability, security, and technology choices. Design answers “how does each piece work internally?” It is concerned with class diagrams, design patterns, algorithms, data structures, and the internal APIs of modules.
The decisions also differ in cost of change. An architectural decision — say, moving from a monolith to microservices — is extremely expensive to reverse once built. A design decision — say, swapping one sorting algorithm for another — is comparatively cheap. Architecture is done first and changes rarely; design happens throughout development and evolves with the code. A common saying: architecture is the parts that are hard to change, design is the parts you can refactor.
3. What is modularity in software design?
Modularity is the practice of dividing a software system into independent, self-contained units called modules, each with a clear responsibility and a well-defined interface. The whole system is assembled from these modules rather than written as one undifferentiated block.
Modularity exists because large systems cannot be held in one person’s head. By decomposing the system, each module can be developed, tested, and understood in isolation, and different people or teams can work on different modules in parallel. It also makes the system maintainable — a change to one module does not ripple across the whole codebase.
Good modularity is measured by coupling and cohesion. Modules should be highly cohesive — everything inside a module belongs together and serves one purpose — and loosely coupled — modules depend on each other as little as possible, communicating only through clean interfaces. The ideal is modules that are independently replaceable: you can swap an implementation without touching the rest of the system. Modularity is the foundation that makes every other good design property achievable.
4. What is abstraction?
Abstraction is the act of hiding complexity behind a simple interface, exposing only what is necessary and concealing implementation details. A user of a module sees what it can do, not how it does it.
In practice, abstraction works at many levels. A function abstracts a sequence of operations behind a name. A class abstracts data and behavior behind methods. An interface abstracts a whole subsystem — the caller of a payment API sees charge(amount), not the banking protocols underneath. Abstraction is why a programmer can use a sorting function without understanding its internal algorithm, or a driver without knowing the hardware.
Abstraction is valuable because it manages complexity and creates stability. The interface — what is exposed — can stay stable while the implementation evolves underneath. This is what enables change: you can rewrite internals without breaking callers. It also enables multiple implementations of the same interface. The risk is leakage: when implementation details escape through the abstraction, its benefit collapses. Good abstraction means the “what” and the “how” are cleanly separated.
5. What is information hiding?
Information hiding is the principle that a module’s internal implementation details should be hidden from everything outside it, exposed only through a controlled interface. Its inventor, David Parnas, argued that each module should encapsulate the design decisions most likely to change.
The point is to contain change. If a design decision is hidden inside a module, it can be changed without affecting anything else. For example, if the choice of data structure for a phone book is hidden behind an interface, swapping a list for a hash map touches only that module. If the decision is exposed — say, callers index directly into the structure — every caller breaks when it changes.
Information hiding is closely related to abstraction but emphasizes secrecy: abstraction gives a clean view, information hiding dictates that what is not in that view is private and untouchable. In code, this is enforced with private fields, encapsulation in objects, and the interface segregation that keeps exposed surface minimal. It is the mechanism that makes abstraction durable — the hidden details are safe to change precisely because they are hidden.
6. What are the SOLID principles?
SOLID is an acronym for five principles of object-oriented design that make software maintainable and extensible.
S — Single Responsibility: a class should have one reason to change, meaning one responsibility. O — Open/Closed: software should be open for extension but closed for modification — add behavior without editing existing code, typically via polymorphism. L — Liskov Substitution: derived classes must be substitutable for their base classes without breaking correctness. I — Interface Segregation: clients should not be forced to depend on interfaces they do not use; split fat interfaces into small, specific ones. D — Dependency Inversion: high-level modules should not depend on low-level modules; both should depend on abstractions, and abstractions should not depend on details.
Together, the principles fight rigidity, fragility, and immobility. They encourage small focused classes (S), polymorphic extension (O, L), narrow interfaces (I), and depending on abstractions rather than concrete implementations (D). They are not rules to apply blindly — applied dogmatically they add ceremony — but as guidelines they keep a growing system from ossifying.
7. What is the difference between high cohesion and low coupling?
Cohesion measures how related the elements inside a module are. Coupling measures how much one module depends on another. The classic goal of good design is high cohesion and low coupling — and the two reinforce each other.
High cohesion means everything inside a module belongs together and serves a single, well-defined purpose. A highly cohesive module is easy to understand, test, and reuse — there are no mixed concerns pulling it in different directions. Low coupling means modules depend on each other minimally, communicating through clean, stable interfaces rather than reaching into each other’s internals.
Low coupling gives independence — a module can be changed, tested, and replaced without dragging the rest of the system along. High cohesion makes that possible, because a module with one clear purpose has a small, predictable interface. The two work together: strongly related things are grouped (cohesion), and weakly related groups talk through narrow interfaces (coupling). High coupling is the enemy — it makes changes ripple across the system — and low cohesion is its sibling problem, making modules unclear and untestable.
8. What is a design pattern?
A design pattern is a reusable, well-tested solution to a recurring problem in software design. It is not finished code — it is a template, a named description of the problem, the solution shape, and the trade-offs involved, that developers adapt to their specific context.
Patterns are usually classified into three families. Creational patterns handle object creation — the Singleton, Factory, Builder, and Prototype patterns manage how objects are instantiated. Structural patterns handle the composition of classes and objects — Adapter, Decorator, Facade, and Composite show how to assemble structures. Behavioral patterns handle communication between objects — Observer, Strategy, Command, and Template Method define how objects interact.
The value of patterns is shared vocabulary and proven design. Saying “use a Strategy pattern here” communicates a design instantly, and the pattern carries lessons learned from countless implementations. The danger is overuse — applying a pattern for its own sake adds complexity. The right mindset: recognize the problem, then choose the pattern that fits, not the other way around.
9. What are commonly used software design patterns?
The most commonly cited patterns appear across interviews and real codebases. Singleton ensures a class has exactly one instance, like a configuration manager. Factory Method (and Abstract Factory) centralize object creation, letting a client create objects without naming the concrete class. Builder constructs complex objects step by step, like building a query or a report with many optional parts.
On the structural side, Adapter lets two incompatible interfaces work together, like wrapping a legacy API. Decorator adds behavior to an object dynamically without changing its class, like layering logging or caching on a service. Facade provides a simple front door to a complex subsystem. Proxy controls access to another object, used for lazy loading or security.
On the behavioral side, Observer defines a one-to-many notification — when a subject changes, all its observers are notified, like a stock ticker. Strategy swaps algorithms at runtime by encapsulating them behind a common interface. Command turns a request into an object, enabling undo and queuing. Template Method defines the skeleton of an algorithm while letting subclasses fill in steps. These appear constantly, so knowing what each solves and when to apply it is core interview material.
10. What is the difference between layered architecture and client-server architecture?
Layered architecture organizes software into horizontal layers, each with a distinct role and depending only on the layer beneath it. The classic three-tier split — presentation, business logic, and data access — is the canonical example. Each layer has a single responsibility and communicates with the layer below through defined interfaces, which keeps concerns separated and makes each layer independently testable and replaceable.
Client-server architecture organizes software into two roles: a client that requests services and a server that provides them, communicating over a network. The client-server split is about where processing happens and who initiates the interaction, not about a hierarchy of abstraction layers.
The two are not mutually exclusive — a client-server system is usually built with layers internally, and a layered system is often deployed client-server. The distinction is the organizing principle: layered architecture separates by abstraction and responsibility, client-server separates by process and location (often with multiple clients sharing one server). Layered answers “how is the software divided internally?”; client-server answers “who calls whom across a network?”
Premium Content
Unlock Design & Architecture and all premium lessons with a subscription.
From ₹199.99/year — See plans