# Rseng Software Design

> Covers designing research software: modularity and separation of concerns, interfaces and coupling, growing from script to package, the rule of three and the reuse ladder, architecture styles for research systems (pipeline, layered, plugin, services), quality-attribute trade-offs, visual design documentation (C4, Mermaid, PlantUML diagrams-as-code) and architecture decision records. Use when starting non-trivial research software, when a script has grown past easy understanding, when the user asks how to structure or architect code, wants an architecture, API or data-flow diagram, or mentions modularity, coupling, design patterns or refactoring toward structure. For file layout and style see rseng-code-quality; for restructuring inherited code see rseng-legacy-code.

- Skill: `fdiblen/rseng-software-design` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add fdiblen/rseng-software-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/fdiblen/rseng-software-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: CC-BY-4.0
- Author: fdiblen (https://skillmd.com/u/fdiblen)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/fdiblen/rseng-software-design

---


# Designing research software

Design is deciding what depends on what - everything else is
detail. Research code has a characteristic failure mode: the
2,000-line script that grew one working line at a time until nobody
can change anything without breaking everything. Design is the
prevention, and it is proportional: an exploration notebook needs
none, a shared analysis package needs some, community
infrastructure needs real architecture. Name the tier (the same
tiering rseng-management-planning and rseng-fairguard use) and design
to it - overdesign wastes research time as surely as underdesign
does.

## The core moves

- Separate concerns ruthlessly: I/O, computation and presentation
  in different functions/modules. The single highest-payoff
  research refactor is splitting "read + compute + plot" scripts -
  it makes the computation testable (rseng-testing), the I/O
  swappable (rseng-scientific-file-formats) and the plotting
  rerunnable (rseng-scientific-visualization).
- Functions over scripts, parameters over globals and edits: code
  a colleague runs with different inputs must take the inputs as
  arguments, not require editing line 12 (configuration files for
  run-level choices - rseng-reproducibility).
- Pure cores, effectful edges: keep the science (the math, the
  model) in pure functions that take data and return data; push
  file paths, printing and state to the boundary. Pure cores are
  the cheapest code to test, parallelize (rseng-hpc-computing) and
  reason about numerically (rseng-numerical-accuracy).
- Interfaces before internals: design what callers see (function
  signatures, data structures) deliberately - internals can be
  rewritten, published interfaces cannot without breaking users
  (rseng-maintenance-sustainability's deprecation discipline).
- Data structures ARE design: choosing "a DataFrame with these
  named columns and units documented" over "parallel lists" does
  more for a codebase than any pattern; make the domain's own
  concepts (Spectrum, Cohort, Grid) explicit types when they recur.

## Coupling and cohesion, the working test

Good structure = things that change together live together; things
that change separately depend on each other as little as possible.
Practical checks an agent can apply while reviewing or writing:

- Can you test this function without a filesystem, network or
  20-minute setup? If not, coupling is too tight.
- If the file format changes, how many modules change? (Should be
  one - rseng-scientific-file-formats.)
- Does module A need A-internals of module B, or only its
  interface? Reaching into internals is the smell.
- Could a colleague reuse the core computation in their pipeline
  without dragging your plotting and paths along?

## Growing structure honestly

Research software evolves script -> module -> package; design
effort follows evidence, not aspiration:

- Extract when it hurts: the second copy-paste, the third
  positional argument nobody remembers, the first "do not touch
  this part" - each is the signal to extract a function or module,
  not before (rseng-legacy-code's seams, applied preemptively).
- Package structure when sharing: the src-layout, entry points and
  API surface come when others will install it
  (rseng-project-scaffolding provides the shape).
- Patterns are vocabulary, not goals: use a known pattern when the
  problem genuinely recurs (strategy for interchangeable models,
  pipeline stages for rseng-workflows shapes); a pattern imposed on
  a problem that does not have it is complexity with a name.

## Modularity that enables reuse

Reusable code is modular code with its assumptions made explicit -
design for the SECOND user from the start, cheaply:

- The rule of three: generalize on the third use, not the first -
  premature generality is speculative complexity, but the third
  copy-paste is the signal to extract the shared unit
  (rseng-software-metrics' duplication numbers find these).
- Extractable by construction: pure cores with explicit inputs (no
  reads of project-global config inside the science function), no
  hardwired paths, units and conventions documented at the interface
  (rseng-defensive-coding) - the difference between "our script" and
  "a function any colleague can import".
- The reuse ladder: function within the project -> module with its
  own tests -> package others install (rseng-project-scaffolding,
  rseng-software-publishing). Climb when demand exists - a colleague
  asking twice IS demand - and note that well-factored modules are
  what make the strangler and migration paths cheap later
  (rseng-legacy-code, rseng-open-source-migration).
- Do not lock reusable parts to the project: a general-purpose
  reader/solver/plotter that imports project internals cannot leave;
  keep the dependency arrow pointing from project to reusable unit,
  never back.
- Reuse others before designing your own (rseng-software-reuse) - the
  most modular design is the module you did not have to write; and
  when you publish a reusable unit, its interface stability becomes
  a promise (rseng-maintenance-sustainability's deprecation
  discipline).

## Architecture: the system level

When the software is a SYSTEM - multiple components, deployments or
consumers - structure needs deciding above the module level. The
styles that recur in research software, each fitting a shape of
problem:

- Pipeline: stages transforming data in sequence - the natural
  architecture for analysis and processing (rseng-workflows is its
  operational form); keep stages independently runnable with
  explicit intermediate formats (rseng-scientific-file-formats).
- Layered: computation core, orchestration, interface - the
  architecture behind "pure cores, effectful edges" scaled up; the
  core must stay importable without the layers above it (a CLI, a
  notebook and a web UI should share one core).
- Plugin: a stable kernel with extension points - the architecture
  of extensible research tools (analysis frameworks, format
  readers, method registries); invest in it when third parties or
  future-you will add capabilities without touching the kernel
  (rseng-community-governance benefits: contributors write plugins,
  not core patches).
- Services: components behind network interfaces - justified by
  independent scaling, deployment or team boundaries, and paid for
  in operational burden; a research group rarely wants five
  services where one process would do.

Choose by quality attributes, stated out loud: what must this
system do well - throughput (rseng-performance-profiling,
rseng-big-data-processing), portability across laptop and cluster
(rseng-hpc-computing), extensibility, auditability of results
(rseng-provenance)? Architecture is the trade among them; a choice
that cannot name the attribute it serves is fashion. Record the
trade in an ADR (below).

Document the architecture at two zoom levels, C4-style: a context
diagram (the system among its users and neighbors) and a container/
component view (the major pieces and their dependencies) - two
small diagrams that stay updatable beat a mural that rots (drawn
as code per the diagram section below; rseng-documentation's
developer-notes section is their home).
Re-draw at milestones; a diagram that no longer matches the code
is a review finding (rseng-code-review).

## Record the decisions

For decisions with lasting consequences - core data structures,
dependency choices (rseng-software-reuse), parallelization strategy,
API shape - write a short architecture decision record (ADR):
context, options, choice, consequences, dated, in the repository
(rseng-documentation). Three paragraphs now saves the archaeology
later (rseng-legacy-code exists because nobody wrote them), and ADRs
are exactly the material design reviews and onboarding need.

## Diagram the design, as code

When designing software, an architecture, an API or a data flow,
produce a visual diagram as part of the documentation - not as a
slide for one meeting. A reader forms a mental model from one good
diagram faster than from pages of prose, and the act of drawing
exposes coupling and unclear ownership while they are still cheap to
fix.

- Prefer diagrams-as-code so diagrams live in the repository, diff in
  review and regenerate with the docs: Mermaid (renders natively on
  GitHub/GitLab and in MkDocs/Sphinx), PlantUML for richer UML and C4
  (C4-PlantUML), Graphviz for generated dependency graphs, D2 or
  draw.io ONLY with the source file committed next to the export.
- Match the diagram to the question. Architecture: C4 context +
  container views (above). API design: a sequence diagram per core
  interaction showing who calls whom in what order, and for HTTP APIs
  the resource/endpoint map next to the OpenAPI spec. Data:
  a flow diagram from raw inputs through processing to published
  outputs (pairs with rseng-provenance's run records), and an
  entity-relationship sketch when there is a schema. State machines
  for anything with lifecycle (jobs, sessions, review states).
- One question per diagram, roughly one screen: a diagram needing a
  legend for its legend answers nothing. Split rather than cram.
- Keep them where the text is: embed in the README or docs page they
  support (Mermaid blocks render inline), store sources under docs/,
  and regenerate exports in the docs build rather than committing
  stale images.
- Keep them true: a diagram that no longer matches the code is a
  review finding (rseng-code-review), exactly like a stale docstring.
  Re-draw at the milestones that change structure, and date what you
  cannot keep current so readers know its era.
- Accessibility: give every embedded diagram a one-paragraph text
  equivalent - the caption IS documentation, and screen readers and
  grep cannot parse boxes (rseng-ux-accessibility).

## Working with this skill

This skill is source-independent: it encodes established software
design practice proportioned for research software. BSSW's design
topic collects the community's deeper material; CodeRefinery
teaches the modular-development moves hands-on.

Learn more (verified):
  - https://bssw.io/items?topic=design - Better Scientific
    Software design resources
  - https://coderefinery.github.io/modular-type-along/ -
    CodeRefinery modular code development
  - https://adr.github.io - architecture decision records
  - https://c4model.com - the C4 model for architecture diagrams
  - https://mermaid.js.org - diagrams-as-code that renders on forges
  - https://plantuml.com - UML and C4 diagrams as text

<!-- related-skills:begin -->

## Related skills

Check whether any of these applies before moving on:

- rseng-documentation - home for ADRs and diagrams
- rseng-hpc-computing - pure cores ease parallelization
- rseng-legacy-code - seams when refactoring existing structure
- rseng-maintenance-sustainability - interface stability and deprecation promises
- rseng-software-reuse - reuse others before designing your own
- rseng-workflows - pipeline style's operational form

<!-- related-skills:end -->

