Best Practices

Paul Schrimpf

2026-03-02

\[ \def\Er{{\mathrm{E}}} \def\En{{\mathbb{E}_n}} \def\cov{{\mathrm{Cov}}} \def\var{{\mathrm{Var}}} \def\R{{\mathbb{R}}} \def\arg{{\mathrm{arg}}} \newcommand\norm[1]{\left\lVert#1\right\rVert} \def\rank{{\mathrm{rank}}} \newcommand{\inpr}{ \overset{p^*_{\scriptscriptstyle n}}{\longrightarrow}} \def\inprob{{\,{\buildrel p \over \rightarrow}\,}} \def\indist{\,{\buildrel d \over \rightarrow}\,} \DeclareMathOperator*{\plim}{plim} \DeclareMathOperator*{\argmax}{argmax} \DeclareMathOperator*{\argmin}{argmin} \]

Overview

Goals

  • Correct
  • Maintainable & Extensible
    • Clear
    • Contained
    • Consistent
  • Efficient

Tools

Goal Tool(s)
Correct tests, static analysis
Maintainable version control, CI, documentation, dependency management
Efficient benchmarks, profiler

Version Control

Version Control

  • essential
  • git is by far the most popular and what I recommend
  • git hosting services
    • github.com
    • gitlab.com
    • bitbucket.org
  • Benefits:
    • backup
    • edit history
    • coordination

git

Julia Tools

Create Packages

PkgTemplates.jl

using PkgTemplates
Template(interactive=true)("SomeNewPackage")

or

using PkgTemplates
tpl = Template(; dir=pwd(),
               user="schrimpf", # github username
               authors=["Paul Schrimpf"],
               plugins=[ProjectFile(), SrcDir(),
                        Tests(project=true, aqua=true, jet=true),
                        License(; name="MIT"),
                        Git(),
                        GitHubActions(),
                        Codecov(),
                        Citation(),
                        Documenter{GitHubActions}(),
                        PkgBenchmark(),
                        Formatter()]
               )
tpl("SomeNewPackage")

BestieTemplate.jl

using BestieTemplate
BestieTemplate.generate("TestPackage")

Dependencies and Where to Install Them

  • In the default/global project: (reached by activate in pkg mode) put utilities that you always want available
  • In each project environment (i.e. activate . when in TestPackage directory) put dependencies that are required for the re-usable code in the package in TestPackage/src
  • Some dependencies are required for a task, like running tests or creating documents, but not strictly needed for everything a package might be used for these go in an environment in TestPackage/test or TestPackage/docs
    • Or edit TestPackage/Project.toml as described in the Pkg docs

My Default Project

(@v1.12) pkg> activate
  Activating project at `~/.julia/environments/v1.12`
(@v1.12) pkg> st
Status `~/.julia/environments/v1.12/Project.toml`
  [6e4b80f9] BenchmarkTools v1.6.3
  [31a5f54b] Debugger v0.7.16
  [b2ad6718] EmacsVterm v0.3.0
  [7073ff75] IJulia v1.34.4
  [5903a43b] Infiltrator v1.9.7
  [5fb14364] OhMyREPL v0.5.32
  [295af30f] Revise v3.13.2
  [0c614874] TerminalPager v0.6.10 [loaded: v0.6.9]

Tests

  • essential
  • organize code into small functions, test them all
  • test both your code and code from others that you rely on
  • use either standard library Tests or TestItems.jl (good VS Code integration)

Documentation

Continuous Integration

  • Automatically execute some actions in the cloud after git commits or pull requests or merges
    • Run tests
    • Build documentation
    • Run static code analysis / Linter
    • Check test coverage
    • etc
  • Many providers
    • GitHub Actions
    • TravisCI
    • etc

Test Coverage

  • Automatically try to determine which lines of code were executed during testing and produce a summary and report
  • app.codecov.io
  • coveralls.io

Static Code Analysis

  • “linters” analyze code to detect errors and possible bugs
    • built into VSCode and other editors
    • JET.jl for detecting type stability problems (advanced)
  • formatters check for following text formatting standards around indentation and such

Debugging

  • adding println, @show, or similar is an okay starting point
  • Infiltrator.jl and Debugger.jl lets you halt execution anywhere and examine variables

Further Reading

  • Gentzkow and Shapiro (2014)
  • Pruim, Gîrjău, and Horton (2023)
  • Wilson (2017)
  • Haider, Riesch, and Jirauschek (2021)
  • Orozco et al. (2020)

References

Gentzkow, Matthew, and Jesse M. Shapiro. 2014. “Code and Data for the Social Sciences: A Practitioner’s Guide.” In. https://api.semanticscholar.org/CorpusID:62408223.
Haider, Michael, Michael Riesch, and Christian Jirauschek. 2021. “Realization of Best Practices in Software Engineering and Scientific Writing Through Ready-to-Use Project Skeletons.” Optical and Quantum Electronics 53 (10): 568. https://doi.org/10.1007/s11082-021-03192-4.
Orozco, Valérie, Christophe Bontemps, Elise Maigné, Virginie Piguet, Annie Hofstetter, Anne Lacroix, Fabrice Levert, and Jean-Marc Rousselle. 2020. “HOW TO MAKE a PIE: REPRODUCIBLE RESEARCH FOR EMPIRICAL ECONOMICS AND ECONOMETRICS.” Journal of Economic Surveys 34 (5): 1134–69. https://doi.org/https://doi.org/10.1111/joes.12389.
Pruim, Randall J., Maria-Cristiana Gîrjău, and Nicholas Jon Horton. 2023. “Fostering Better Coding Practices for Data Scientists.” https://arxiv.org/abs/2210.03991.
Wilson, Jennifer AND Cranston, Greg AND Bryan. 2017. “Good Enough Practices in Scientific Computing.” PLOS Computational Biology 13 (6): 1–20. https://doi.org/10.1371/journal.pcbi.1005510.