SWDev Story

SWDev Story: Software Design

concept, architecture, specification, documentation

1.0 What is Software Design?

Software design is the process of deciding what a software system should do, how it should be structured, and how its parts should communicate - before writing the bulk of the code. A good design makes the intent of the system visible, keeps components loosely coupled, and produces documentation that guides both implementation and future maintenance. Design need not be a large bureaucratic exercise. Its scope should match the project. For a small project like TextFinder, a day or two of thinking and a few pages of notes is enough. For a system with dozens of developers, a more formal process is warranted. In both cases the core questions are the same:
  • Who will use this, and what do they need to accomplish?
  • What components are needed, and what are their responsibilities?
  • How do the components communicate and share data?
  • What constraints (performance, security, cost) must the design satisfy?

1.1 Concept Development

Concept development is the earliest design activity. Its goal is to answer the question: what problem are we solving? Good concept development involves:
  • User analysis - who are the users, what are their goals, and what activities do they perform?
  • Use cases - brief narratives describing how users interact with the system to accomplish specific goals.
  • Constraint identification - performance targets, platform requirements, budget and schedule limits, security and compliance obligations.
  • Scope definition - what the system will and will not do, stated clearly enough that the team can build to it.
For TextFinder, concept development is brief: the user is a developer who needs to find text matching a regex in a directory tree. The scope is command-line input, recursive directory traversal, and matched-line output. The constraints are: fast enough for interactive use, portable across Rust/C++/C#/Python.

1.2 Architecture

Architecture describes the high-level structure: what packages (modules, libraries, assemblies) exist, what each is responsible for, and how they depend on one another. A clean architecture has a few key properties:
  • Single responsibility - each component does one thing well. DirNav traverses directories; TextSearch matches text; Executive coordinates them.
  • Directed dependencies - dependencies flow in one direction, typically from higher-level coordinators down to lower-level utilities. Cycles between packages make code harder to test and reason about.
  • Defined interfaces - components communicate through explicit interfaces or type signatures, not through shared global state.
  • Testability - components with clear boundaries and injected dependencies can be unit-tested without spinning up the whole system.
TextFinder's architecture consists of four packages:
TextFinder Package Architecture
Package Responsibility
Executive Parses command-line arguments and coordinates the other packages.
DirNav Recursively traverses a directory tree and invokes a callback for each matching file.
TextSearch Opens a file, applies the regex, and returns matched lines.
Display Formats and writes matched results to standard output.

1.3 Specification

A specification states precisely what each component must do - its inputs, outputs, preconditions, postconditions, and error behavior. Specifications are the contract between the designer and the implementer. A useful specification for a function or method describes:
  • Signature - parameter types, return type
  • Preconditions - what must be true before the call
  • Postconditions - what is guaranteed after a successful call
  • Error conditions - what happens when inputs are invalid or operations fail (panic, return an error type, throw an exception)
Example - DirNav specification (language-independent):
DirNav::new(root: path, extensions: list<string>) -> DirNav
  pre:  root exists and is a directory; extensions is non-empty
  post: DirNav ready to traverse; no filesystem access yet

DirNav::search(visitor: fn(path)) -> Result
  pre:  DirNav constructed; visitor accepts a file path
  post: visitor called once for each file matching extensions
  err:  returns Err if root is not accessible
Specifications need not be this formal. What matters is enough precision that an implementer does not have to guess, and a reviewer can tell whether the implementation is correct.

1.4 Design Documentation

Documentation serves two audiences: the team building the software now, and maintainers working on it later. Useful design documents include:
  • Architecture diagrams - package dependency graphs, data flow diagrams, sequence diagrams for key interactions.
  • Interface tables - one row per function, listing its signature, preconditions, and postconditions.
  • Design rationale - why a key decision was made, and what alternatives were considered. This is the most valuable documentation because it cannot be recovered from the code alone.
  • Inline comments - for non-obvious constraints, subtle invariants, or workarounds for specific bugs. Code should not need comments to explain what it does, only why it does something surprising.
For small projects, a single README or wiki page covering architecture and key decisions is usually sufficient. Keep documentation close to the code - in the repository itself - so it stays in sync with changes.

1.5 References

Software Design References
ResourceDescription
Refactoring Guru Illustrated catalog of design patterns with examples in multiple languages.
Martin Fowler - Architecture Articles on software architecture patterns, microservices, and design tradeoffs.
SW Design Bites: Introduction The Design Bites sequence from this track - concept through structure.
SWDev Projects TextFinder and CodeAnalyzer project specifications and implementations.