Readable code and project structure
Use this skill when writing or reviewing research code for readability, or
when laying out a project's directories. Both goals serve the same end:
code is read far more often than it is written (a commonly cited read/write
ratio is 7:1), so anything that helps a future reader - including the
original author months later - directly improves reusability, the "R" in
the FAIR research software principles.
Favour conventions others already recognise over clever, project-specific
inventions.
Make code readable
Apply these rules when authoring or reviewing code:
- Use descriptive names for variables, functions, classes, and modules
that explain their purpose. Avoid single-letter names outside tight
loops or well-known math notation.
- Format consistently: consistent indentation and spacing throughout.
Consistency within a project or module matters more than any global
ideal - when joining existing code, adopt the conventions already in
place rather than imposing your own.
- Separate code into sections with blank lines (between classes,
functions, logical blocks) so structure is visible at a glance.
- Keep lines short. Prefer many short lines over few long ones - blocks
that are horizontally short and vertically long are easier to scan.
- Use indentation to show hierarchy and mark the beginning and end of
control structures.
- Add type annotations (type hints) for untyped or dynamically typed
languages such as Python or JavaScript, and validate them with a type
checker (e.g. mypy for Python). Type checking catches faulty assumptions
as well as faulty annotations.
- Write informative comments and docstrings that convey intent and
context - what the code is for and why - not a restatement of the
syntax. Comment where logic is not self-evident.
Follow a style guide
- Adopt the community-standard style guide for the language rather than
inventing one: PEP 8 for Python, the Google R style guide for R; Google's
style guides cover Python, Java, R, C++, and Shell.
- A style guide is a shared set of conventions so everyone's contributions
look similar. Consistency across the project is the point.
- When contributing to an existing project, match its existing style even
if it differs from your preference.
Automate style and quality checks
Do not enforce style by hand - let tools do it:
- Run an auto-formatter to apply style mechanically (e.g. Black for Python).
This removes formatting from code review entirely.
- Run a linter - static analysis that flags inconsistencies, stylistic
errors, suspicious constructs, and some real bugs (e.g. Pylint for
Python).
- Lean on editor/IDE support: VS Code, PyCharm, RStudio, and Eclipse can
check conformance as you type, warn on deviations, and often autocorrect.
- Wire formatters and linters into pre-commit hooks and CI so every change
is checked automatically, not just when someone remembers.
Pre-commit: the guardrail framework
The pre-commit framework runs the checks automatically at commit
time, from one versioned config - the difference between "we have a
linter" and "nothing unlinted lands":
- Anatomy: a
.pre-commit-config.yaml at the repo root lists hook
repositories PINNED to revisions; start from the basics (trailing
whitespace, end-of-file, merge-conflict markers, large-file guard),
add the ecosystem's linter/formatter (ruff for Python), a secret
scanner (rseng-security), and project-specific local hooks where a
custom check earns automation.
- Adoption on an existing repo: install the hooks, then run
pre-commit run --all-files ONCE in its own commit - a dedicated
formatting commit keeps blame readable (the same reasoning as
avoiding repo-wide reformats in rseng-legacy-code).
- Keep hooks fast: commit-time checks have a seconds budget - slow
checks (test suites, type-checking large trees) belong in CI, not
in the hook, or people bypass hooks entirely and the guardrail is
gone.
- Update deliberately:
pre-commit autoupdate bumps pinned hook
revisions - treat it like any dependency update, on a cadence with
green tests (rseng-dependency-management).
- SKIP honestly: skipping a hook (SKIP=) is legitimate when the
hook itself is broken or inapplicable to the change - record why
in the commit; skipping to silence a real finding just moves the
failure to CI or review.
- Mirror in CI: run the same hooks in the pipeline
(
pre-commit run --all-files as a CI step or pre-commit.ci) so a
locally bypassed hook (--no-verify) cannot land unchecked
(rseng-ci-cd) - local hooks are convenience; CI is enforcement.
Write modular, reusable code
- Split code into small functions that each achieve a single, clear
purpose - easier to read, test, and reuse.
- Group related functions into reusable libraries and packages.
- Reach for established design patterns for common, well-defined problems
instead of reinventing a solution; this saves time and raises quality.
- Prefer existing, well-tested libraries for common tasks (e.g. reading and
writing standard data formats) over bespoke, error-prone code.
Structure the project directory
A clear, conventional layout lets people (and you) locate things fast, keep
code, config, and data separate, isolate issues quickly, and reproduce
results - especially valuable for long-term or collaborative work. There is no single official standard,
but the following is widely understood.
Put everything for the project in a single, meaningfully named directory.
Keep auxiliary information and metadata at the top level so others can tell
what the project does and how to reuse it:
README - what the project is, how to install and run it, how to
reproduce results.
LICENSE - the reuse terms.
CITATION.cff - how to cite the project (Citation File Format).
codemeta.json or a similar metadata standard - machine-readable
software metadata.
Organise the rest into sub-directories labelled by content type:
src/ (or code, scripts) - source code.
data/ - data, with raw kept separate and never modified: e.g.
data/raw, data/clean, data/processed. This prevents overwriting or
losing the original.
results/ - analysis outputs, tables, summary statistics.
figures/ (or fig) - generated plots and figures (or fold these into
results/).
doc/ - documentation and guides.
papers/, presentations/, or references/ - literature; consider
keeping these in a separate project so papers do not mix with a reusable
software package.
- Add a nested
README or LICENSE inside a sub-directory when its terms
differ (for example, code and data licensed differently).
Name files and directories consistently
- Use standard, self-explanatory directory names (as above).
- Avoid spaces and special characters - they break tools; use underscores
or hyphens and stay consistent.
- Name files to reflect their contents; let version control track versions
rather than encoding
_v2, _final in filenames.
Version-control the project
- Put the whole project under version control in its own repository; at
minimum version-control code and data sub-directories, plus anything
written by hand (docs, manuscripts) rather than generated.
- Untrack files too large or too sensitive to expose (use
.gitignore in
Git); never version-control passwords or secrets.
- Use tags/releases to mark specific versions (a journal submission, a
dissertation version) instead of proliferating dated filenames.
Scaffold new projects with tooling
- For Python, a src-layout with
src/your_package/, tests/,
pyproject.toml, README, and .gitignore is the common modern shape.
- Poetry manages dependencies, virtual environments, versioning, and
publishing from a single
pyproject.toml, and scaffolds a new project
directory; it works well with the recommended src layout.
- Project templates/skeletons enforce a standardised layout and modern
tooling, cutting setup time and keeping teams consistent.
A concrete reference stack (as encoded by the NLeSC python-template;
see rseng-project-scaffolding): ruff as the single linter, formatter and
import sorter; EditorConfig for cross-editor consistency; SonarCloud
for hosted static analysis; and first-class type checking with Pyright
(or Mypy) - treat type checks as part of linting, not an optional
extra.
Working with this skill
The generated references.md beside this file lists the source
material and pointers:
- references.md - verified Learn more pointers
Learn more (verified):
Related skills
Check whether any of these applies before moving on:
- rseng-documentation - docstrings and README quality
- rseng-fair-software - readability serves reusability
- rseng-language-guides - per-language style and tool choice
- rseng-legacy-code - repo-wide reformat cautions
- rseng-project-scaffolding - templates encode this tooling baseline
- rseng-software-metrics - quantifying complexity and duplication
1---2name: rseng-code-quality3description: Covers writing readable research code and structuring software projects: naming, formatting, style guides, linters and formatters, pre-commit hooks, modular design, and a conventional directory layout with top-level metadata files. Use when the user asks how to make code readable or clean, pick or enforce a style guide, set up linting/formatting or pre-commit, name variables and functions, organise a repo, or decide where files and data go. For generating a new project from a maintained template see rseng-project-scaffolding; for quantitative complexity and duplication measurement see rseng-software-metrics; for architecture-level structure see rseng-software-design.4license: CC-BY-4.05---67# Readable code and project structure89Use this skill when writing or reviewing research code for readability, or10when laying out a project's directories. Both goals serve the same end:11code is read far more often than it is written (a commonly cited read/write12ratio is 7:1), so anything that helps a future reader - including the13original author months later - directly improves reusability, the "R" in14the FAIR research software principles.15Favour conventions others already recognise over clever, project-specific16inventions.1718## Make code readable1920Apply these rules when authoring or reviewing code:2122- Use descriptive names for variables, functions, classes, and modules23 that explain their purpose. Avoid single-letter names outside tight24 loops or well-known math notation.25- Format consistently: consistent indentation and spacing throughout.26 Consistency within a project or module matters more than any global27 ideal - when joining existing code, adopt the conventions already in28 place rather than imposing your own.29- Separate code into sections with blank lines (between classes,30 functions, logical blocks) so structure is visible at a glance.31- Keep lines short. Prefer many short lines over few long ones - blocks32 that are horizontally short and vertically long are easier to scan.33- Use indentation to show hierarchy and mark the beginning and end of34 control structures.35- Add type annotations (type hints) for untyped or dynamically typed36 languages such as Python or JavaScript, and validate them with a type37 checker (e.g. mypy for Python). Type checking catches faulty assumptions38 as well as faulty annotations.39- Write informative comments and docstrings that convey intent and40 context - what the code is for and why - not a restatement of the41 syntax. Comment where logic is not self-evident.4243## Follow a style guide4445- Adopt the community-standard style guide for the language rather than46 inventing one: PEP 8 for Python, the Google R style guide for R; Google's47 style guides cover Python, Java, R, C++, and Shell.48- A style guide is a shared set of conventions so everyone's contributions49 look similar. Consistency across the project is the point.50- When contributing to an existing project, match its existing style even51 if it differs from your preference.5253## Automate style and quality checks5455Do not enforce style by hand - let tools do it:5657- Run an auto-formatter to apply style mechanically (e.g. Black for Python).58 This removes formatting from code review entirely.59- Run a linter - static analysis that flags inconsistencies, stylistic60 errors, suspicious constructs, and some real bugs (e.g. Pylint for61 Python).62- Lean on editor/IDE support: VS Code, PyCharm, RStudio, and Eclipse can63 check conformance as you type, warn on deviations, and often autocorrect.64- Wire formatters and linters into pre-commit hooks and CI so every change65 is checked automatically, not just when someone remembers.6667## Pre-commit: the guardrail framework6869The pre-commit framework runs the checks automatically at commit70time, from one versioned config - the difference between "we have a71linter" and "nothing unlinted lands":7273- Anatomy: a `.pre-commit-config.yaml` at the repo root lists hook74 repositories PINNED to revisions; start from the basics (trailing75 whitespace, end-of-file, merge-conflict markers, large-file guard),76 add the ecosystem's linter/formatter (ruff for Python), a secret77 scanner (rseng-security), and project-specific `local` hooks where a78 custom check earns automation.79- Adoption on an existing repo: install the hooks, then run80 `pre-commit run --all-files` ONCE in its own commit - a dedicated81 formatting commit keeps blame readable (the same reasoning as82 avoiding repo-wide reformats in rseng-legacy-code).83- Keep hooks fast: commit-time checks have a seconds budget - slow84 checks (test suites, type-checking large trees) belong in CI, not85 in the hook, or people bypass hooks entirely and the guardrail is86 gone.87- Update deliberately: `pre-commit autoupdate` bumps pinned hook88 revisions - treat it like any dependency update, on a cadence with89 green tests (rseng-dependency-management).90- SKIP honestly: skipping a hook (SKIP=<id>) is legitimate when the91 hook itself is broken or inapplicable to the change - record why92 in the commit; skipping to silence a real finding just moves the93 failure to CI or review.94- Mirror in CI: run the same hooks in the pipeline95 (`pre-commit run --all-files` as a CI step or pre-commit.ci) so a96 locally bypassed hook (--no-verify) cannot land unchecked97 (rseng-ci-cd) - local hooks are convenience; CI is enforcement.9899## Write modular, reusable code100101- Split code into small functions that each achieve a single, clear102 purpose - easier to read, test, and reuse.103- Group related functions into reusable libraries and packages.104- Reach for established design patterns for common, well-defined problems105 instead of reinventing a solution; this saves time and raises quality.106- Prefer existing, well-tested libraries for common tasks (e.g. reading and107 writing standard data formats) over bespoke, error-prone code.108109## Structure the project directory110111A clear, conventional layout lets people (and you) locate things fast, keep112code, config, and data separate, isolate issues quickly, and reproduce113results - especially valuable for long-term or collaborative work. There is no single official standard,114but the following is widely understood.115116Put everything for the project in a single, meaningfully named directory.117118Keep auxiliary information and metadata at the top level so others can tell119what the project does and how to reuse it:120121- `README` - what the project is, how to install and run it, how to122 reproduce results.123- `LICENSE` - the reuse terms.124- `CITATION.cff` - how to cite the project (Citation File Format).125- `codemeta.json` or a similar metadata standard - machine-readable126 software metadata.127128Organise the rest into sub-directories labelled by content type:129130- `src/` (or `code`, `scripts`) - source code.131- `data/` - data, with raw kept separate and never modified: e.g.132 `data/raw`, `data/clean`, `data/processed`. This prevents overwriting or133 losing the original.134- `results/` - analysis outputs, tables, summary statistics.135- `figures/` (or `fig`) - generated plots and figures (or fold these into136 `results/`).137- `doc/` - documentation and guides.138- `papers/`, `presentations/`, or `references/` - literature; consider139 keeping these in a separate project so papers do not mix with a reusable140 software package.141- Add a nested `README` or `LICENSE` inside a sub-directory when its terms142 differ (for example, code and data licensed differently).143144## Name files and directories consistently145146- Use standard, self-explanatory directory names (as above).147- Avoid spaces and special characters - they break tools; use underscores148 or hyphens and stay consistent.149- Name files to reflect their contents; let version control track versions150 rather than encoding `_v2`, `_final` in filenames.151152## Version-control the project153154- Put the whole project under version control in its own repository; at155 minimum version-control code and data sub-directories, plus anything156 written by hand (docs, manuscripts) rather than generated.157- Untrack files too large or too sensitive to expose (use `.gitignore` in158 Git); never version-control passwords or secrets.159- Use tags/releases to mark specific versions (a journal submission, a160 dissertation version) instead of proliferating dated filenames.161162## Scaffold new projects with tooling163164- For Python, a src-layout with `src/your_package/`, `tests/`,165 `pyproject.toml`, `README`, and `.gitignore` is the common modern shape.166- Poetry manages dependencies, virtual environments, versioning, and167 publishing from a single `pyproject.toml`, and scaffolds a new project168 directory; it works well with the recommended src layout.169- Project templates/skeletons enforce a standardised layout and modern170 tooling, cutting setup time and keeping teams consistent.171172A concrete reference stack (as encoded by the NLeSC python-template;173see rseng-project-scaffolding): ruff as the single linter, formatter and174import sorter; EditorConfig for cross-editor consistency; SonarCloud175for hosted static analysis; and first-class type checking with Pyright176(or Mypy) - treat type checks as part of linting, not an optional177extra.178179## Working with this skill180181The generated references.md beside this file lists the source182material and pointers:183184- references.md - verified Learn more pointers185186187Learn more (verified):188 - https://peps.python.org/pep-0008/ - PEP 8 style guide for Python code189 - https://google.github.io/styleguide/ - Google style guides for190 many languages191 - https://docs.astral.sh/ruff/ - Ruff Python linter and formatter192 - https://pre-commit.com - pre-commit hook framework193 - https://editorconfig.org - consistent editor settings across tools194195196<!-- related-skills:begin -->197198## Related skills199200Check whether any of these applies before moving on:201202- rseng-documentation - docstrings and README quality203- rseng-fair-software - readability serves reusability204- rseng-language-guides - per-language style and tool choice205- rseng-legacy-code - repo-wide reformat cautions206- rseng-project-scaffolding - templates encode this tooling baseline207- rseng-software-metrics - quantifying complexity and duplication208209<!-- related-skills:end -->