ArchitectMind
  1. Home
  2. Tutorials
  3. Book 5 · Software Design & Design Patterns
  4. Volume I
  5. Software Design
Book 5 · Volume I — Software Design Foundations & Principles · Topic 1.1

Software Design

What design is, how it differs from implementation and architecture, and its high- and low-level forms

10 min readStage 2 · Frameworks & design8 interview questions
Chapter
Chapter 1 — What Is Software Design?
Prerequisites
Comfortable with Java classes, interfaces and packages (Book 1)
You will be able to
Define software design precisely, place a decision at the architecture, HLD, LLD or implementation level, and produce both views for a feature
Covers
What is software design? · Design vs implementation · Design vs architecture · Low-level design · High-level design
On this page
  1. 1. Concept / Definition
  2. 2. Why It Exists
  3. 3. Core Concepts
  4. 4. How It Works Internally — Design as a Process
  5. 4A. Deep Dive — How Much Design Up Front?
  6. 5. APIs / Features / Syntax — Java Features That Express Design
  7. 6. Practical Examples
  8. 7. Common Problems & Mistakes
  9. 8. Performance & Production Considerations
  10. 9. Architecture & Design Considerations
  11. 10. Interview Questions & Answers

1Concept / Definition

Software design is the activity — and the result — of deciding how a software system is organised into parts, what each part is responsible for, how parts interact and depend on each other, and which details each part hides. The outcome of design is a structure: modules, classes, interfaces, data models and the relationships between them.

Where design sits between requirements and code
Figure 1 — Where design sits between requirements and code

Three properties characterise design decisions:

PropertyMeaningExample
StructuralDecides parts and relationships, not individual statements"Pricing rules live in a PricingPolicy separate from Order"
ConsequentialAffects many future changes, not just the current oneChoosing to pass Money objects instead of double everywhere
ConstrainingLimits what is easy or hard to do laterMaking Order immutable forces state changes through new instances
Table 1 — What makes a decision a design decision

2Why It Exists

In the 1960s–70s software grew beyond what one person could hold in their head, and the "software crisis" — late, buggy, unmaintainable projects — made clear that writing correct statements was not enough. Structured design (Stevens, Myers and Constantine, 1974), information hiding (Parnas, 1972) and later object-oriented design (Booch, Meyer, Gamma et al.) introduced explicit ways of deciding structure. Agile practices (from 2001) did not abolish design; they made it continuous and evolutionary — "just enough" upfront, refined through refactoring.

3Core Concepts

3.1 What is software design?

Design answers four recurring questions for any piece of software:

QuestionDesign outputJava expression
What are the parts?Modules, components, classesPackages, classes, records, JPMS modules
What is each part responsible for?Responsibilities, invariantsClass purpose, method set, validation
How do parts collaborate?Interfaces, messages, data flowInterfaces, method signatures, events
What does each part hide?Encapsulated decisionsprivate members, non-exported packages
Table 2 — Four questions of design

Design operates under forces: functional requirements, quality requirements (performance, security), constraints (team, technology, deadline) and expected change. Good design balances these forces; there is no context-free "correct" design (Topic 1.4).

3.2 Design vs implementation

Design versus implementation
Figure 2 — Design versus implementation

Implementation is the realisation of a design in code: algorithms, idioms, error handling, library usage. The two are intertwined but answer different questions.

JAVA
// Design decision: price calculation is a separate, replaceable policy
public interface PricingPolicy {
    Money priceFor(Cart cart, Customer customer);
}
 
// Implementation decision: how this particular policy computes the price
public final class TieredPricingPolicy implements PricingPolicy {
    @Override
    public Money priceFor(Cart cart, Customer customer) {
        Money subtotal = cart.lines().stream()
                .map(CartLine::total)
                .reduce(Money.ZERO, Money::plus);
        return customer.isGold() ? subtotal.times(0.9) : subtotal;
    }
}
AspectDesignImplementation
Scope of impactMany classes, many future changesOne method or class
Cost to change laterHigherLower
Typical review question"Is this the right place for this responsibility?""Is this code correct and clear?"
ArtefactsInterfaces, types, package structure, diagrams, ADRsMethod bodies, tests
Table 3 — Design and implementation compared

A correct implementation can sit inside a poor design (logic duplicated across five services), and a good design can have buggy implementations (fixed locally). Code reviews should examine both.

3.3 Design vs architecture

Architecture is the subset of design decisions that are significant — expensive to change, cross-cutting, or tied to key quality attributes. Grady Booch put it as: architecture represents the significant design decisions, where significance is measured by cost of change.

Levels of design decisions
Figure 3 — Levels of design decisions
DecisionLevelWhy
Split the system into order, billing and shipping servicesArchitectureAffects deployment, teams, data ownership
Use asynchronous events between servicesArchitectureAffects consistency, availability, tooling
OrderService exposes place(PlaceOrder) and publishes OrderPlacedHigh-level designComponent interface and interaction
Order aggregates OrderLines and enforces a maximum quantityLow-level designClass responsibility and invariant
Use a stream().reduce() to compute the totalImplementationLocal coding choice
Table 4 — The same feature at four levels

The boundary is fuzzy and context-dependent: choosing a date/time type is implementation in one class, but architecture if it defines the time model across fifty services.

3.4 Low-level design

Low-level design (LLD) specifies the inside of a component: classes, their fields and methods, relationships (association, composition, inheritance), algorithms and data structures, and the patterns applied. It is what interviewers mean by "design a parking lot" or "design an LRU cache".

JAVA
// LLD sketch: Order aggregate with an invariant and intention-revealing methods
public final class Order {
    private static final int MAX_LINES = 50;
    private final OrderId id;
    private final List<OrderLine> lines = new ArrayList<>();
    private OrderStatus status = OrderStatus.DRAFT;
 
    public Order(OrderId id) { this.id = Objects.requireNonNull(id); }
 
    public void addLine(Product product, int quantity) {
        if (status != OrderStatus.DRAFT) {
            throw new IllegalStateException("Order not editable");
        }
        if (lines.size() == MAX_LINES) throw new IllegalStateException("Too many lines");
        lines.add(new OrderLine(product, quantity));
    }
 
    public Money total() {
        return lines.stream().map(OrderLine::total).reduce(Money.ZERO, Money::plus);
    }
 
    public void submit() {
        if (lines.isEmpty()) throw new IllegalStateException("Empty order");
        status = OrderStatus.SUBMITTED;
    }
 
    public List<OrderLine> lines() { return List.copyOf(lines); }
}
LLD elementTypical notation
Classes and relationshipsUML class diagram
Interaction over timeUML sequence diagram
Lifecycle of an objectState diagram
Invariants and contractsJavadoc, assertions, types
Table 5 — Low-level design artefacts

3.5 High-level design

High-level design (HLD) describes a system or subsystem in terms of components (services, modules, databases, external systems), their interfaces, and data and control flow between them. It sits between architecture and LLD and is what "system design" interviews focus on at component level.

The same feature at HLD and LLD zoom levels
Figure 4 — The same feature at HLD and LLD zoom levels
HLD elementQuestion answered
Component diagramWhich components exist and who talks to whom?
Interfaces / API definitionsWhat operations and data cross each boundary?
Data ownershipWhich component owns which data?
Key flowsHow does a request move through components?
Non-functional mappingWhere are caching, retries, security applied?
Table 6 — High-level design questions

4How It Works Internally — Design as a Process

1. Understand forces: requirements, quality goals, constraints, expected change.

2. Identify concepts from the domain (nouns, verbs, rules) and candidate responsibilities.

3. Assign responsibilities to components (HLD) and then classes (LLD) — keeping related responsibilities together and volatile decisions hidden.

4. Define interfaces between the parts, including contracts and error behaviour.

5. Validate with scenarios ("what if tax rules change?"), prototypes and tests.

6. Implement and refine: refactoring continuously improves the design as understanding grows.

Design proceeds top-down (decomposing a system) and bottom-up (extracting abstractions from working code) at the same time. Test-driven development is one technique that drives LLD from the caller's perspective.

4ADeep Dive — How Much Design Up Front?

ApproachWhen it fitsRisk
Big design up front (BDUF)Safety-critical, contractual, hard-to-change hardware interfacesDesigns for imagined requirements; late feedback
Emergent design (TDD + refactoring only)Small teams, uncertain domain, easy deploymentArchitectural decisions made accidentally
Just enough design up frontMost product teamsRequires judgement about what is "significant"
Table 7 — Design approaches

A practical rule: decide up front what is expensive to change (service boundaries, data ownership, public APIs, persistence model) and let the rest emerge through TDD and refactoring. Record significant decisions in ADRs (Book 4, Topic 10.7).

4A.1 Design documentation that stays useful

ArtefactLevelKeep it alive by
C4 context/container diagramsArchitecture/HLDGenerating from code or reviewing each quarter
Module/package diagramsHLD/LLDArchUnit/Spring Modulith generation
ADRsAll levelsWriting at decision time, never editing history
Javadoc on public typesLLDTreating it as part of the contract
Table 8 — Documentation per level

5APIs / Features / Syntax — Java Features That Express Design

Java featureDesign intent it expresses
interface, sealed interfaceAbstractions and closed sets of variants
recordImmutable value/data carriers
private, package-privateEncapsulation and information hiding
JPMS module-info.java (exports, requires)Module boundaries enforced by the compiler
final classes and fieldsPrevent unintended extension/mutation
GenericsReusable, type-safe abstractions
Pattern matching switchExhaustive handling of sealed hierarchies
Table 9 — Design-supporting language features
JAVA
// A sealed hierarchy documents a design decision: exactly these payment types exist
public sealed interface Payment permits CardPayment, BankTransfer, Voucher { }
public record CardPayment(String token, Money amount) implements Payment { }
public record BankTransfer(String iban, Money amount) implements Payment { }
public record Voucher(String code, Money amount) implements Payment { }
 
Money fee(Payment p) {
    return switch (p) {                        // compiler checks exhaustiveness
        case CardPayment c -> c.amount().times(0.015);
        case BankTransfer b -> Money.of("0.20");
        case Voucher v -> Money.ZERO;
    };
}

6Practical Examples

6.1 One feature, four levels

Level"Customers can pay by voucher"
ArchitectureVouchers issued by a separate Promotions service; payments service calls it
HLDPaymentService → VoucherClient.redeem(code); PaymentCompleted event
LLDVoucher record in sealed Payment hierarchy; VoucherRedemption handles idempotency
ImplementationHTTP client with timeout; retry once on 503; map error codes to exceptions
Table 10 — Feature decomposed by level

6.2 Recognising a hidden design decision in code

JAVA
// Before: design decision (tax rate) buried in implementation, repeated in 4 places
double total = subtotal * 1.19;
 
// After: decision made explicit and located in one place
public interface TaxPolicy { Money taxOn(Money net, Region region); }
Money total = net.plus(taxPolicy.taxOn(net, customer.region()));

6.3 LLD interview template

StepWhat to produce
1. ClarifyUse cases, constraints, scale, out of scope
2. EntitiesCore classes/records and their responsibilities
3. RelationshipsComposition, association, interfaces
4. Key flowsSequence of calls for main use cases
5. Extension pointsWhere variation is expected (Strategy, State…)
6. Edge casesConcurrency, invalid input, failure
Table 11 — Structuring a low-level design answer

6.4 HLD review checklist

  • Every component has a clear responsibility and owner.
  • Every piece of data has exactly one owning component.
  • Interfaces are documented with error behaviour and versioning.
  • Main flows are shown as sequences, including failure paths.
  • Quality requirements (latency, availability) are mapped to mechanisms.

7Common Problems & Mistakes

  • Equating design with diagrams — diagrams drift; code is the truth.
  • No design at all ("we're agile") — structure emerges accidentally and expensively.
  • Designing for imagined futures — speculative generality (Topic 1.4).
  • Mixing levels in one discussion — debating naming while service boundaries are undecided.
  • Architecture by technology choice — picking frameworks before understanding responsibilities.
  • Treating LLD as "just coding" — skipping responsibility assignment inside components.

8Performance & Production Considerations

Design choiceProduction effect
Component boundariesNumber of network hops, latency budget
Data ownershipQuery patterns, need for replication/caching
Object model (mutable vs immutable)Thread safety, GC pressure
Interface granularityChatty vs coarse calls, payload sizes
Table 12 — Design decisions visible at runtime

Performance-critical paths are design decisions too: batching, caching and asynchronous processing are hard to retrofit if interfaces assume one item per synchronous call.

9Architecture & Design Considerations

  • Architecture is design with a high cost of change — invest review effort accordingly.
  • Keep levels consistent: HLD component boundaries should be visible in LLD packages and modules.
  • Make design explicit in code: types, modules and names should reveal the intended structure.
  • Evolve deliberately: refactor as understanding grows; record significant decisions.

10Interview Questions & Answers

Q1Basic

What is software design?

Software design is the set of decisions about how a system is structured: how it is divided into parts, what each part is responsible for, how the parts interact and depend on each other, and what each part hides from the others. These decisions exist whether or not anyone makes them deliberately; code that grows without design still has a structure, just an accidental one. Good design makes the likely changes cheap. For example, hiding the payment provider behind an interface means switching providers touches one class. Design happens at every level, from method signatures to service boundaries.

Q2Basic

What is the difference between design and implementation?

Design decides what the parts of a system are and how they relate: responsibilities, interfaces, dependencies and data ownership. Implementation realises those decisions in concrete code: algorithms, data structures, error handling and the lines of each method. The difference is mostly about reach. A design decision, such as splitting order and payment logic into separate modules with a defined interface, affects many future changes and is expensive to reverse. An implementation decision, such as using a loop or a stream inside one method, affects only local code and can be changed cheaply. The line between them is blurry, but the cost of change is a good guide.

Q3Intermediate

What is the difference between high-level and low-level design?

High-level design describes a system in terms of its major components: services or modules, their responsibilities, the interfaces between them, the data each owns and how requests and data flow through the system. It answers questions such as "which component handles payments and how does checkout call it?" Low-level design zooms into one component: its classes and interfaces, their methods and relationships, the algorithms and the data structures. It answers questions such as "how is a discount calculated and which class owns that rule?" Both are needed, and they must stay consistent; a clean high-level diagram is worthless if the code inside ignores it.

Q4Intermediate

How does architecture differ from design?

Architecture is the subset of design decisions that are significant for the system as a whole. A decision is architectural if it is expensive to change later, affects many parts of the system, or determines key quality attributes such as performance, availability or security. Choosing between a modular monolith and microservices, deciding which service owns customer data or selecting an event-driven integration style are architectural; naming a class or choosing a collection type are design but not architecture. The distinction matters because architectural decisions deserve more analysis, wider review and written records such as ADRs, while ordinary design decisions can evolve freely.

Q5Senior Engineer

How much design should be done up front in an agile team?

Do enough design up front to settle the decisions that are expensive to reverse, and let the rest emerge. Before building, agree on module or service boundaries, data ownership, public APIs and contracts with other teams, and the approach to key quality attributes such as security and scalability. Inside those boundaries, let detailed design evolve through test-driven development and continuous refactoring, guided by real requirements rather than speculation. Record significant decisions in ADRs so they can be revisited deliberately. The aim is neither a big design phase nor no design at all, but just enough design at the right time.

Q6Senior Engineer

How do Java language features support design?

Modern Java gives designers precise tools. Interfaces define abstractions that hide implementations. Sealed interfaces and classes model a closed set of variants, such as the possible payment results, and pattern-matching switches over them are checked for exhaustiveness by the compiler. Records model immutable values with value-based equality, making value objects such as Money or OrderId cheap to write. Access modifiers, package structure and the module system enforce encapsulation, so internal classes cannot be used from outside. Using these features deliberately makes design intentions visible in code and lets the compiler catch violations that would otherwise be found in reviews or production.

Q7Technical Architect

How do you keep HLD and LLD consistent over time?

Make the high-level structure visible in the code itself. Map each component in the high-level design to a module, package or build module with the same name, so the code mirrors the diagram. Enforce the intended dependencies automatically, using the Java module system, ArchUnit tests or Spring Modulith verification, so a violation fails the build rather than drifting silently. Generate component diagrams from the code where possible, instead of maintaining them by hand. Finally, review changes to module interfaces and dependencies more carefully than ordinary changes, for example by requiring an architect's approval or an ADR for new cross-module dependencies.

Q8Solution Architect

How would you run a design review for a new capability?

Prepare the review around the forces and goals first: the business capability, key quality attributes such as latency and availability, and constraints such as compliance or existing systems. Then review the high-level design: components, data ownership, interfaces, integration style and how failures are handled end to end. Spot-check the low-level design of the core domain classes, where the most important rules live. Identify the decisions that are hard to reverse, discuss alternatives and their consequences, and record them in ADRs. Keep the review time-boxed, invite the people who will build and run the system, and agree on concrete follow-up actions with owners.

Newsletter

Get new tutorials every week

New ArchitectMind tutorials and interview questions, once a week. No spam, unsubscribe anytime.

Almost done! Check your inbox and click the confirmation link to complete your subscription.
Keep going in Book 5

Software Design & Design Patterns

Volume I · Topic 1.2

Why Software Design Matters

Software Design Foundations & Principles

Volume I · Topic 1.3

Good vs Poor Design

Software Design Foundations & Principles

Volume II · Topic 1.1

Objects

Object-Oriented Design & SOLID

Volume II · Topic 1.2

Classes

Object-Oriented Design & SOLID

Volume II · Topic 1.3

Relationships

Object-Oriented Design & SOLID

Volume III · Topic 1.1

What Is a Design Pattern?

Creational & Structural Design Patterns

Volume III · Topic 1.2

Pattern Categories

Creational & Structural Design Patterns

Volume III · Topic 1.3

Pattern vs Framework

Creational & Structural Design Patterns

Volume IV · Topic 1.1

Strategy Pattern

Behavioral Design Patterns

Volume IV · Topic 1.2

Strategy in Java

Behavioral Design Patterns

Volume IV · Topic 1.3

Strategy vs Conditional Logic

Behavioral Design Patterns

Volume V · Topic 1.1

Layered Architecture

Enterprise & Application Design Patterns

Volume V · Topic 1.2

Service Layer

Enterprise & Application Design Patterns

Volume V · Topic 1.3

Repository

Enterprise & Application Design Patterns

Volume VI · Topic 1.1

What Is Refactoring?

Refactoring, Code Quality & Evolutionary Design

Volume VI · Topic 1.2

Why Refactor?

Refactoring, Code Quality & Evolutionary Design

Volume VI · Topic 1.3

Refactoring Safety

Refactoring, Code Quality & Evolutionary Design

Volume VII · Topic 1.1

Method-Level Design

Advanced Design & Architecture-Level Design Decisions

Volume VII · Topic 1.2

Class-Level Design

Advanced Design & Architecture-Level Design Decisions

Volume VII · Topic 1.3

Module-Level Design

Advanced Design & Architecture-Level Design Decisions