I am developing what I think would be called an application. It is meant for a novice end user that can just change some inputs and then run. It will compute some quantities and then generate a report with text and plot outputs. I would like some input on proper code organization which will be 1.) performant, 2.) organize namespaces/dependencies, and 3.) maintain usability.
This application includes a package and the code to run it in the same repository. It is currently organized as below.
MyPackage ├──run # Part of the repository but not formally part of the package │ ├──main.jl # using MyPackage; using OtherPackages1; define inputs; include("run*.jl") │ ├──runfracture.jl # read fracture data; call fracture functions; produce nice outputs; │ ├──runlocalfailure.jl # read local failure data; call local failure functions; produce nice outputs; │ ├──runpackagemanager.jl # import Pkg; Pkg.activate("PathToMyPackage"); Pkg.instantiate() │ ├──runvalidation.jl # read validation data; call validation functions; produce nice outputs; ├──src │ ├──MyPackage.jl # module MyPackage; export functions; using OtherPackages2; include ("*.jl") │ ├──auxiliary.jl # contains functions needed by other package files/functions │ ├──fracture.jl # contains fracture functions │ ├──localfailure.jl # contains local failure functions │ ├──validation.jl # contains validation functions ├──test │ ├──manualtesting.jl # where I tinker │ ├──runtest.jl # formal test wrapper │ ├──testset.jl # formal test set ├──Manifest.toml ├──Project.toml ├──README.md
The end user would typically only change inputs in
main.jl, but could also modify
run*.jl to change plot labels or other small tweaks.
Thoughts and Issues
Functional Programming and Global Variables
runfiles are growing into large procedural scripts with lots of global variables as I make the outputs prettier and the inputs more flexible. I know these scripts should be broken down into small functions, but then I don’t know where to put them. What is an acceptable number/size of global variables? Do I just make the whole script a function and call it from
include("runvalidation.jl")? I could move
runfunctions into the package
src, but they would be too specific to be reusable. Maybe I need to make a
RunMyPackagesub-package? Maybe wrapping everything in one big
main()function is the easiest solution? Ideally, the code inside
runwould be simple enough for someone who doesn’t know Julia to still be able to follow along.
Namespaces and Dependencies
I thus far haven’t bothered with separate namespaces, but I think I need to start. The obvious choice is to make
validation.jltheir own modules since they are independent apart from
auxiliary.jl, but then I wondered if that was necessary since they just contain function definitions. Likewise, should the independent
runvalidation.jlbe separated by modules even if those scripts become functions when I fix Issue #1. Does passing inputs through functions eliminate the need to worry about collisions and modules? How would modules in the
runscripts interact with modules in the package
Ease of Use
My current instructions for users say to copy the
rundirectory from the package location to a project folder containing the required data, modify
main.jlwith proper local paths to the data, and then run
main.jl. This works well enough, but I wasn’t sure if there was a more common/streamlined procedure. The
DrWatsonpackage probably has the best approach, but my end users won’t necessarily know Julia or Git.