Curated Tech Reading Map

Find your next tech book to read

Domain

Software Design

A systematic learning path for software design — from clean code to architecture, testing, and quality culture. Covers code quality, TDD, DDD, distributed system design, and DevOps/quality organization.

The Terrain of This Field

The terrain of software design can be read as a single ascending path: it begins at "readable code" under your feet, rises through the habits that preserve quality (refactoring and test-driven development), and continues up to the highlands of "architecture" that overlook the whole system.

The first continent is the realm of "code quality" — variable names, functions, and responsibilities at the micro scale. Beyond it spread the hills of "testing and refactoring" for growing code without breaking it, and further on rise the mountain ranges of "clean architecture" and "domain-driven design," where you design the direction of dependencies and the boundaries between modules.

Deeper still lie the plateaus of "scale and operations" — distributed systems, data-intensive design, and DevOps/quality organizations. The key to surveying this field is recognizing that the scale of concerns connects continuously, from the micro (code) to the macro (organization and operations).

Follow the arrows to read in order / solid = required, dashed = recommended

Share this map

Difficulty

Beginner
Intermediate
Advanced

Dependencies

Prerequisite
Recommended
Related

Computing layout...

Books in this domain

Difficulty
Reading status
Tags

39 books

Dependencies

  1. 01Building MicroservicesBuilding Applications with AI AgentsRelated / From microservices to multi-agent systems

    Reason: Learning to design 'autonomous parts that coordinate' with microservices, the idea carries over to multi-agent design that coordinates several AI agents. Practical AI Agent Development becomes an entry point for applying the sense of loose coupling, communication, and responsibility division cultivated in service decomposition to AI systems.

  2. 02The DevOps HandbookSoftware Engineering at GoogleRecommended / From DevOps culture to engineering culture at scale

    Reason: After learning DevOps culture and practices in 'The DevOps Handbook', move to a concrete example of how a hyperscale organization operates them over the long term. 'Software Engineering at Google' frames engineering as 'programming integrated over time', offering insight into large-scale, long-lived engineering culture as a cross-domain bridge.

  3. 03Testing JavaScript ApplicationsFull Stack TestingRelated / From JavaScript testing strategy to a full-stack quality strategy

    Reason: Once you've established a JavaScript testing strategy, you broaden your view beyond the frontend to whole-system quality optimization including CI/CD. Full Stack Testing provides a quality strategy spanning contract, performance, security, and E2E, positioning testing as part of development culture.

  4. 04Building Micro-FrontendsBuilding MicroservicesRelated / From frontend decomposition to its backend counterpart

    Reason: Grasp the idea of splitting the frontend into independent parts, and you notice it shares the same roots as backend microservices. Building Microservices provides the server-side framework of boundaries, autonomous deployment, and alignment with organization, letting you understand the principles of decomposition from both sides.

  5. Reason: After learning the techniques of safety evaluation via benchmarks and red-teaming, you broaden your view to how the act of 'evaluating' concretely gets built into your own generative-AI product. This book on generative-AI application evaluation covers everything from building an evaluation perspective model to confusion matrices, RAG-oriented metrics, security evaluation, and AI agent evaluation along the development lifecycle, connecting the abstract idea of safety evaluation to your own product's quality-assurance process.

  6. 06Clean CodeRefactoringRecommended / From code quality principles to safe structural refactoring

    Reason: Once you know what good code looks like, you need techniques to safely move existing code toward that standard. Refactoring offers a systematic way to improve structure without changing behavior, guided by code smells.

  7. 07Clean CodeTest-Driven DevelopmentRecommended / From code quality to the discipline of test-driven development

    Reason: To keep writing clean code, you need a foundation that lets you change it without fear. Test-driven development instills the discipline of growing clean, working code in small cycles, turning quality into a habit.

  8. 08Clean CodeClean ArchitectureRecommended / From code quality to architecture

    Reason: Once quality at the function and class level is second nature, the next step is to design the direction of dependencies and boundaries across the whole system. Clean Architecture provides the principle of pushing details outward and keeping business rules at the core.

  9. 09Head First Design PatternsDesign PatternsRecommended / From an introduction to patterns to the original text

    Reason: After grasping patterns intuitively through illustrations and examples, you solidify the vocabulary with the original text, where intent, applicability, and consequences are rigorously defined. The Gang of Four codifies patterns into a shared design language that grounds discussion.

  10. 10Test-Driven DevelopmentUnit Testing Principles, Practices, and PatternsRecommended / From the TDD cycle to principles of test design

    Reason: Once the small TDD cycle makes writing working tests second nature, you advance to principles of what and at what granularity to test. Good tests are resistant to refactoring, do not get in its way, and reliably catch regressions.

  11. 11Test-Driven DevelopmentGrowing Object-Oriented Software, Guided by TestsRecommended / From TDD basics to TDD sustained across a system

    Reason: After getting comfortable with unit-level TDD, you move to outside-in TDD—driving from acceptance tests down to units—to design the collaboration between objects itself. Mocks become a tool for expressing collaboration, not just a stand-in for implementations.

  12. 12RefactoringWorking Effectively with Legacy CodeRecommended / From refactoring to code that has no tests yet

    Reason: Once safe refactoring moves are in hand, you take on the hardest case: legacy code without tests. You first find seams to break dependencies, cover them with tests as a safety net, and only then improve the structure.

  13. 13Clean ArchitectureFundamentals of Software Architecture, 2nd EditionRecommended / From architectural principles to systematic study

    Reason: After gaining an intuition for the ideal shape from Clean Architecture, you formalize architectural characteristics and trade-offs into a shared vocabulary. This book elevates design judgment from personal instinct to a discipline you can reason about.

  14. 14Clean ArchitectureLearning Domain-Driven DesignRecommended / From architecture to an introduction to DDD

    Reason: Once you can control the direction of dependencies, the next question becomes where to draw the boundaries. Domain-Driven Design derives bounded contexts from the language of the business, giving architectural decomposition real meaning.

  15. 15Fundamentals of Software Architecture, 2nd EditionBuilding MicroservicesRecommended / From architecture fundamentals to distributed design

    Reason: Once you have a vocabulary of architectural characteristics and trade-offs, you apply that judgment to the concrete case of distributed systems. Microservices demand a design that deliberately breaks coupling in exchange for independent deployment and autonomy.

  16. 16Fundamentals of Software Architecture, 2nd EditionSoftware Architecture: The Hard PartsRecommended / From fundamentals to the hard design decisions

    Reason: After grasping the fundamental characteristics, you advance to trade-off analysis in the hard parts that have no single right answer—service granularity, data ownership, distributed transactions. The same authors show not the answer but how to decide.

  17. 17Fundamentals of Software Architecture, 2nd EditionBuilding Evolutionary ArchitecturesRecommended / From fundamentals to evolutionary architecture

    Reason: After learning static architectural decisions, you widen your view to designs that keep adapting to change. You gain the idea of using fitness functions to continuously verify architectural characteristics, making evolution measurable.

  18. 18Learning Domain-Driven DesignDomain-Driven Design: Tackling Complexity in the Heart of SoftwareRecommended / From an introduction to DDD to the original text

    Reason: After mapping the strategic and tactical landscape with an introductory text, you verify the origin and precise definitions of the concepts in the source. Evans' DDD grounds the ubiquitous language and model-driven design, forming the foundation for applied judgment.

  19. 19Building MicroservicesMonolith to MicroservicesRecommended / From microservice fundamentals to migration strategy

    Reason: Once you understand microservice principles, you advance to realistic strategies for migrating from an existing monolith. Most successes peel services off a monolith incrementally, so you also learn when not to distribute from the start.

  20. 20Building MicroservicesDesigning Data-Intensive ApplicationsRecommended / From distributed design to data systems

    Reason: Once you split services, the next hard problem becomes the distribution, consistency, and reliability of data. DDIA systematically provides the foundational theory and trade-offs of distributed data systems—replication, partitioning, and transactions.

  21. 21Unit Testing Principles, Practices, and PatternsWorking Effectively with Legacy CodeRelated / From test design to improving legacy code

    Reason: After grasping the principles of good tests, you apply them to legacy code that resists testing. You introduce seams to break dependencies and pin current behavior with characterization tests before changing it safely.

  22. Reason: Once you can judge the quality of an individual test, you extend to a design approach that grows object responsibilities and collaboration through tests. Choosing the right test double becomes feedback on the design itself.

  23. 23Clean ArchitectureA Philosophy of Software Design, 2nd EditionRecommended / From architecture to a philosophy of managing complexity

    Reason: Once you have the principles of dependencies and boundaries, you descend to the universal question beneath them: how to contain complexity. Ousterhout systematizes complexity management through the lens of deep modules and information hiding.

  24. 24RefactoringA Philosophy of Software Design, 2nd EditionRelated / From tactical refactoring to the strategy of managing complexity

    Reason: Once you have tactical means of improvement, you raise your view to the strategic question of why—the nature of complexity itself. A new criterion enters your refactoring decisions: does this actually reduce complexity?

  25. 25Unit Testing Principles, Practices, and PatternsEffective Software TestingRecommended / From testing principles to test design techniques

    Reason: After grasping the qualities of good tests—protection against regressions, resistance to refactoring, fast feedback—you raise the question of what to test with systematic techniques like boundary analysis, equivalence partitioning, and structural testing. Case selection moves from intuition to engineering.

  26. 26Effective Software TestingProperty-Based Testing with PropEr, Erlang, and ElixirRecommended / From test design techniques to verifying invariants

    Reason: Once example-based testing techniques are in hand, you advance to property-based testing, which generates inputs broadly and verifies invariants. You let the machine find counterexamples a human would miss and expose gaps in the spec.

  27. 27Unit Testing Principles, Practices, and PatternsFull Stack TestingRecommended / From unit testing to a full-stack quality strategy

    Reason: Once unit tests form a solid base, you stack integration, contract, and end-to-end tests in the right proportions along the test pyramid, scaling up to a system-wide quality strategy. The rule is many at the bottom, few at the top.

  28. 28Full Stack TestingAccelerateRecommended / From a CI/CD strategy to measuring delivery performance

    Reason: Once testing and CI/CD give you a foundation for building in quality, you frame how it affects organizational delivery performance through measurement. DORA's four key metrics show empirically that speed and stability are not a trade-off.

  29. 29AccelerateSoftware Engineering at GoogleRecommended / From delivery metrics to the engineering culture that sustains them at scale

    Reason: Once you can improve delivery performance through measurement, you widen your view to the engineering culture and practices that sustain it at scale and over time. Google frames this as 'programming integrated over time.'

  30. 30Software Engineering at GoogleLeading QualityRecommended / From engineering culture to leading on quality

    Reason: After understanding the engineering practices of large organizations, you advance to a leadership view that raises quality from individual effort to a culture the organization cultivates. Quality becomes a strategic concern of management.

  31. 31Full Stack TestingLeading QualityRelated / From full-stack quality to the culture that sustains it

    Reason: Once you have a technical quality strategy in hand, you widen your view to how to embed it at the team and organizational level. The subject shifts from the quantity and kinds of tests to decision-making and culture around quality.

  32. 32Clean ArchitectureTeam TopologiesRecommended / From architecture to the teams that shape it

    Reason: Once you can design technical boundaries, you widen your view to the fact that those boundaries and team structure shape each other—Conway's Law. Architecture is not only a code problem but an organizational one.

  33. 33Team TopologiesDomain-Driven Design: Tackling Complexity in the Heart of SoftwareRecommended / From team boundaries to domain models

    Reason: After designing team boundaries for fast flow, you back those boundaries with the language of the business domain. A bounded context defines the outline of both the team and the model at once.

  34. 34Monolith to MicroservicesTeam TopologiesRecommended / From splitting the monolith to team boundaries

    Reason: As you carve out services, the question becomes whether the unit of decomposition matches the cognitive load of a team. The inverse Conway maneuver reorganizes teams to fit the architecture you want.

  35. 35Building Evolutionary ArchitecturesSoftware Architecture: The Hard PartsRecommended / From evolutionary design to the hard parts of distribution

    Reason: Once you have the idea of guiding evolution with fitness functions, you advance to concrete analysis of the hard parts of distributed architecture—granularity, data ownership, and communication trade-offs—from the same authors.

  36. 36Growing Object-Oriented Software, Guided by TestsEffective Software TestingRecommended / From advanced TDD practice to test design techniques

    Reason: After practicing how to grow design through tests, you raise the sufficiency of the test cases themselves with systematic techniques. You turn both wheels—good design and thorough cases—to reduce gaps in testing.

  37. Reason: Once you have universal principles for containing complexity, you apply them specifically to the complexity of the domain. DDD tames the essential complexity inherent in the business through a model and a ubiquitous language.

  38. 38Clean CodeDomain-Driven Design: Tackling Complexity in the Heart of SoftwareRecommended / From code quality to domain modeling

    Reason: Once you have quality standards at the code level, you point that expressiveness toward the language of the domain. Meaningful names become a ubiquitous language that bridges business and code, making the model speakable in code.

  39. 39Clean ArchitectureBuilding MicroservicesRecommended / From architectural principles to distributed implementation

    Reason: The principles of dependency direction and boundaries apply directly to designing service boundaries. You connect them to concrete decisions that map bounded responsibilities onto independently deployable units.

  40. Reason: The principles of test design carry over to the frontend. Building on the test pyramid, you cover the essentials of the UI with a few end-to-end tests and protect logic densely with unit tests—the criteria stay the same across layers.

  41. 41The Art of Readable CodeClean CodeRecommended / From readable code to a systematic view of code quality

    Reason: After grasping the basics of readability such as naming and function decomposition, you move on to binding them into principles of why. Clean Code connects individual techniques into a coherent system of craftsmanship and design principles.

  42. 42The Art of Readable CodeThe Pragmatic ProgrammerRecommended / From readable code to the pragmatic craft

    Reason: Once you have the concrete techniques of readability such as naming and formatting, you widen your view to the professional philosophy that runs through daily decisions—DRY, orthogonality, and a stance toward change. The Pragmatic Programmer binds individual techniques into an attitude of why.

  43. 43The Art of Readable CodeRefactoringRelated / From readable code to safe, incremental refactoring

    Reason: Once you know what readable code looks like, you need techniques to move existing code toward that shape without breaking it. Refactoring offers a step-by-step way to improve structure in small moves without changing behavior, guided by code smells.

  44. 44The Pragmatic ProgrammerClean CodeRecommended / From pragmatic craft to code quality principles

    Reason: After adopting the pragmatic mindset of keeping code healthy, you ground those attitudes in concrete quality standards at the function and class level. Clean Code systematizes what good code is through principles and worked examples.

  45. 45The Pragmatic ProgrammerRefactoringRelated / From pragmatic discipline to the practice of refactoring

    Reason: Refactoring is how the pragmatic discipline of not living with broken windows is applied to real code. It turns 'spot the decay, fix it now' into a habit of small, behavior-preserving improvements.

  46. 46Clean ArchitecturePatterns of Enterprise Application ArchitectureRecommended / From Clean Architecture to the PoEAA original

    Reason: Once you have the principles of layering and dependency direction, you advance to the source of the canonical patterns that implement them—Repository, Service Layer, Domain Model. Mapping principles to patterns deepens your design vocabulary.

  47. Reason: After mastering enterprise patterns that assume a single database, you widen your view to consistency, replication, and partitioning of data spread across many nodes. The assumptions of scale change.

  48. 48Design PatternsPatterns of Enterprise Application ArchitectureRelated / From GoF patterns to enterprise patterns

    Reason: After learning general-purpose object patterns, you advance to patterns that address enterprise-specific contexts—layering, persistence, and distribution. Fowler's PoEAA supplies that vocabulary, such as Repository and Domain Model.

  49. 49Head First Design PatternsRefactoringRelated / From pattern recognition to incremental refactoring technique

    Reason: Once you can recognize patterns, techniques for safely moving existing code toward the pattern it wants to be become valuable. Refactoring lets you introduce patterns not in one leap but as a sequence of incremental, safe transformations.

  50. 50Property-Based Testing with PropEr, Erlang, and ElixirFull Stack TestingRelated / From property-based testing to a full-stack quality strategy

    Reason: Once you can verify invariants, you place that skill within a quality strategy spanning unit, integration, and end-to-end. You design what property each layer guarantees, giving the whole pyramid coherence.

  51. 51Domain-Driven Design: Tackling Complexity in the Heart of SoftwareData-Oriented ProgrammingRelated / From DDD persistence to immutable, data-driven design

    Reason: After learning to express a domain model with objects, you encounter the contrasting data-oriented idea of separating code from data and taming complexity with immutable data, relativizing the assumptions of your design.

  52. 52A Philosophy of Software Design, 2nd EditionData-Oriented ProgrammingRelated / From managing complexity to a data-oriented alternative

    Reason: Once you have general principles for managing complexity, you examine data-oriented programming as one concrete prescription—separating data from behavior and embracing immutability. The same goal admits different paths.

  53. 53Head First Design PatternsWorking Effectively with Legacy CodeRelated / From recognizing patterns to improving legacy code

    Reason: When you can read the structure of existing code through the lens of patterns, you can move on to building a safe foothold in untested legacy code—finding seams, covering them with tests, and improving the design from there.

  54. 54Design PatternsClean ArchitectureRelated / From the GoF pattern catalog to architectural principles

    Reason: After acquiring a vocabulary of class- and object-level patterns, you widen your view to governing the direction of dependencies and boundaries across the whole system. Clean Architecture pushes details outward and keeps business rules at the center.

  55. 55Learning Domain-Driven DesignTeam TopologiesRecommended / From DDD boundaries to designing team structure

    Reason: Once you can draw bounded contexts, you map those boundaries onto team structure. Aligning contexts with teams lowers cognitive load and speeds the flow of value.

  56. 56Software Architecture: The Hard PartsDesigning Data-Intensive ApplicationsRecommended / From hard design decisions to distributed data systems

    Reason: When the hard parts of distributed architecture force decisions about decomposing data, you descend to the underlying theory of distributed data. The principles of consistency, replication, and storage engines become the grounds for your design decisions.

  57. 57Software Engineering at GoogleGenerative AI for Software DevelopmentRecommended / From Google's engineering practices to AI-assisted development

    Reason: After understanding the engineering practices of large organizations, you view how generative AI changes the flow of development with the same discipline. AI is a tool that pays off only on a foundation of review, testing, and design judgment.

  58. 58The Pragmatic ProgrammerGenerative AI for Software DevelopmentRecommended / From pragmatic practice to working with AI tools

    Reason: Once you have the pragmatic mindset—sharpen your tools, own your judgment—you take in generative AI as a new tool. The ability to verify AI output rather than trust it blindly is the very craftsmanship of the AI era.

  59. 59Generative AI for Software DevelopmentResponsible Software EngineeringRecommended / From using AI in practice to building responsibly

    Reason: Once you can bring AI into development, you advance to the question of responsibility—the impact that power has on society. You take a view that weaves correctness, bias, and accountability of outputs into design from the start.

  60. 60Building Secure and Reliable SystemsResponsible Software EngineeringRecommended / From safety and reliability to social responsibility

    Reason: Once you can build security and reliability into a system's design, you extend that reach to responsibility toward users and society. Technical robustness becomes the foundation for fulfilling ethical and social responsibility.

  61. Reason: Once you have a grasp of domain models and bounded contexts, you take that thinking into the field of large-scale legacy modernization. This book ties domain decomposition to business goals and team structure, turning it into concrete steps for driving a modernization effort.

  62. 62Team TopologiesArchitecture ModernizationRecommended / From team structure to modernization in practice

    Reason: After grasping team structure and Conway's Law, you apply that thinking to the concrete context of legacy modernization. This book combines team topologies with Wardley mapping and domain decomposition, laying out a path to drive modernization across technology, organization, and strategy.

  63. Reason: Once you have test design techniques like boundary analysis and equivalence partitioning, you apply that thinking to a new kind of target: generative AI applications, whose outputs are probabilistic and whose behavior is complex. This book covers evaluation methods specific to generative AI, from building an evaluation perspective model to confusion matrices and RAG-oriented metrics.

  64. Reason: After learning to use generative AI as a development tool, you face the next question: how do you measure the quality of an application that itself embeds generative AI? This book covers evaluation methods along the development lifecycle, from LLM-as-a-Judge to security evaluation and AI agent evaluation.

  65. Reason: After comparing AI tools across the whole development process — code generation, review, test automation, documentation — and gaining criteria for 'which tool, when,' you naturally want to pick one and go deep hands-on. Claude Code Practical Guide builds up, from setup and conversational operation to extension via MCP servers and skills, focused on a single AI coding agent, turning the broad view of tool selection into concrete operational skill you can use in practice.

  66. Reason: Even after getting a map of bounded contexts and strategic/tactical patterns from 'Learning Domain-Driven Design,' it's still hard to picture what to put where in your own code. 'Learning DDD Through Practice' bridges that gap by building an online bookstore in TypeScript, working through concrete decisions like referencing aggregates by ID.

  67. Reason: Even after grounding the origin of the ubiquitous language and model-driven design in Evans' original text, it's hard to picture how value objects and aggregates translate into actual class design. 'Learning DDD Through Practice' shows one worked example all the way through in TypeScript, translating the original concepts back into the language of implementation.

  68. 68Test-Driven DevelopmentxUnit Test PatternsRecommended / From the rhythm of writing tests first to patterns for test code

    Reason: Test-Driven Development: By Example builds the habit of writing tests first, through a multi-currency exercise and a small xUnit framework written from scratch. xUnit Test Patterns takes the resulting test code as its subject: it organizes fixture setup and teardown, result verification and test doubles as patterns, and uses a catalog of 18 test smells to trace problems to their causes. It belongs after the habit is in place, when the tests have to be kept maintainable.

  69. 69xUnit Test PatternsUnit Testing Principles, Practices, and PatternsRelated / From a catalog of test-code patterns to criteria for a test's value

    Reason: xUnit Test Patterns lays out how test code can be written as 68 patterns and 18 smells, aiming at coverage of the options. Unit Testing Principles, Practices, and Patterns gives a vocabulary for judging whether an individual test is worth keeping, built on four pillars: protection against regressions, resistance to refactoring, fast feedback and maintainability. Knowing the options for test doubles and fixtures from the first book, you can use the four pillars of the second to compare them.

  70. 70Patterns of Enterprise Application ArchitectureEnterprise Integration PatternsRecommended / From design inside one application to integration between applications

    Reason: Patterns of Enterprise Application Architecture deals with how to layer an enterprise application, organize its domain logic and map objects onto a relational database. Enterprise Integration Patterns takes up what comes next: connecting such applications to each other through asynchronous messaging, in 65 patterns. The two share a way of presenting patterns as a catalog, so the reading habits built on the first book carry over.

  71. 71Working Effectively with Legacy CodeKill It with FireRecommended / From improving legacy code to deciding whether to modernize the system

    Reason: Working Effectively with Legacy Code covers the techniques for getting untested code under test by finding seams, and changing it safely. Kill It with Fire works at the level outside that: whether an aging system should be rebuilt at all, whether a migration will add value, and how to tell when a project is finished, argued through case studies. Read with the code-level techniques already in hand, its alternatives to a rewrite are easier to picture concretely.

  72. 72Kill It with FireArchitecture ModernizationRecommended / From whether to modernize to how to carry it out

    Reason: Kill It with Fire concentrates on judgment: whether to modernize, where maintenance effort pays off, and how to keep an effort's momentum. Architecture Modernization treats modernization as a socio-technical problem and supplies methods for carrying it out, from finding out what the business is trying to achieve through Wardley Mapping and EventStorming to identifying domains and subdomains. The first book settles whether to do it; the second provides tools for doing it.

Explore next

Connected fields