Design Principles#

If you’re developing your own routines, feel free to ignore any and all of these points.

If you’re trying to understand how the library was designed or to push changes back to master, the following design principles apply. These are about how to think about the library; for the concrete formatting and naming rules that CI enforces, see Code Guidelines.

  1. Every function and class should have a doc string, describing what it does, what it takes and what it returns. Docstrings follow the numpy convention and are checked by pydocstyle: see Code Guidelines for the required format and the exact command to run.

  2. Aim to test every line of the library with multiple examples. Merge requests that add lots of untested code should not be merged. You can learn more about testing in Testing with Pytest.

  3. Jupyter notebooks should be used first-and-foremost as an interface only, and not for defining functionality. Large functions should be moved to the torx library where they can be reused and tested. One exception is plotting routines which often cannot be easily generalized.

  4. Use Jupyter notebooks for examples/documentation. If you have developed a new use-case for the library, add a notebook showing how to perform and interpret the analysis. Notebooks can contain markdown sections: use these!

  5. Prefer a notebook over a new CLI script for one-off or interactive analysis work. A notebook documents itself alongside its output and is run and reviewed the same way as any other TorX example. Reserve scripts/ for tooling that genuinely has to run from the command line, such as a diff utility invoked in CI or from a shell pipeline, not for analysis that could just as well be a notebook cell.

  6. For functions which are specialized to a single code (i.e. GRILLIX or GENE-X), put these in specializations.

  7. Try to write routines which are as flexible as possible. If you only need a single array from a data construct, it’s best to pass the array rather than the whole construct since it makes it easier for others to use your class.

  8. Use run-time checking for everything that isn’t performance-critical. Use assert often, and check types with isinstance. It’s easier to debug code which fails quickly.

  9. Functions should rarely print to the terminal. Communicate through the return value, not stdout. Reserve print for cases where the message is genuinely useful beyond what the return value conveys, such as progress on a long-running trace. Use warnings.warn, not print, to flag a problem the caller should notice. If a function does print routinely, gate it behind a verbose/silent argument rather than printing unconditionally, following the pattern in storx’s set_silent / set_silent_load.

  10. Refactor frequently and aggressively. If you don’t understand or like a particular bit of code, rewrite it, or delete it. If some functionality is no longer used, delete it. No functionality is above review or revision.