-
Notifications
You must be signed in to change notification settings - Fork 4
Coding Standards
Paul Carvalho edited this page Jul 15, 2025
·
9 revisions
This is a set of coding standards to follow to ensure that FishSET is well-organized and easily maintainable.
- Variables: use snake_case across the entire app (example:
user_input). DONOT use camelCase for variables. - UI elements: name UI components using a prefix to indicate the type of element. For example,
input_nameoroutput_plot. - Reactive variables: name with prefix
rv_(example:rv_data). - Buttons: name buttons with suffix
_btn(example:run_model_btn). - Modules: name of module with suffix
_uior_server(exmaple:zone_closure_uiandzone_closure_server)
- Modularize logic: break code into small, reusable functions, especially if logic is reused across different parts of the app. For example, move data preprocessing into its own function in a separate R script in the
/Rfolder. - Use shiny modules: Split the UI and server logic into Shiny modules. This improves scalability, reusability, and maintainability. For example,
mod_input_ui,mod_input_server.
- Minimize reactivity: keep the number of reactive expressions (
reactive(),observe(),observeEvent()) to a minimum to avoid performance issues. - Avoid nested reactivity (if possible): if a reactive value depends on other reactive values, try to simplify the hierarchy of dependencies to reduce complexity.
- Use
req(): always check for the presence of input values usingreq()in server-side logic to ensure that no invalid inputs are processed.
- Use
bslib::page_fillable(),bslib::page_fluid(),fluidRow,fillable = TRUE, fill = TRUE: make the UI responsive with these functions to ensure proper layout in different screen sizes. - Keep UI clean and minimal: avoid overloading the user interface with too many elements. Only include essential inputs, outputs, and information.
- use CSS document to add themes and other styling elements
- Use appropriate UI elements: choose the right UI element for the task. For example, UI input categories.
- Use
session$onSessionEnded(): clean up any long-running processes or background tasks when the session ends. - Use
shinyjsorshinybusyfor asynchronous operations: for better user experience, indicate busy states or process long-running tasks in the background. - Caching and memorization: use
reactiveCacheor similar strategies to cache expensive operations when the input parameters have not changed. - Avoid unnecessary
rendercalls: don't wraprender*functions around things that don't need to be reactive, like static text. - Efficient state management: use
reactiveVal()andreactiveValues()instead of re-triggering reactivity each time a value needs to be updated across the app. - Use the profiler on computationally expensive tasks to optimize code.
- For computationally expensive tasks that disable the shiny while running, use ExtendedTask to run the function in the background and allow users to continue in the app.
- Document your code: user clear and concise comments to explain sections of code, especially for complex logic. Use roxygen-style documentation for functions in separate files.
- Describe inputs/outputs: for each reactive input or output, make sure to describe what it does, and document the expected input and output types.
- Use
bslibfor improved UI: use thebslibpackage for more polished user interfaces. - Use
shinytestto testing: write unit tests for Shiny app with theshinytestpackage to ensure the app works as expected.
- Use
tryCatch(): when there is a chance that an error might occur (e.g., data loading or processing), use thetryCatch()function to handle errors. - Custom error messages: provide clear, user-friendly error messages in the UI for cases where things go wrong (e.g., when inputs are invalid).
- Use Git: this is essential for collaboration, tracking changes, and maintaining the app over time. Try to keep pull requests to a manageable size. If the changes made in a pull request are extensive it can be difficult to review. In this case, break up the pull request/issue into smaller sections.
- Use
.gitignore: avoid committing large or unnecessary files like data files or temporary files created during development by adding them to.gitignore.
- Test files are located in the FishSET/tests/testthat folder - aim to have unit tests for each script in the R/ folder (and tests for each function in the script if there are multiple).
- Test file naming convention is
test-[rscript].R. For example, the test file associated with thesql_functions.Rscript istest-sql_functions.R. Files can be automatically generated and placed within the testthat folder by running theusethis::use_test("Rscript.R")function. - Shinytest2 tests are skipped during the automated R CMD check because the temporary testing environment is too restrictive. Run all unit tests including shinytest2 tests by running
devtools::test()or click on test in the build tab in RStudio. - The
test-shiny-app.Rscript in FishSET/tests/testthat run shiny tests defined in the FishSET/inst/ShinyFiles/MainApp/tests/testthat/test-shinytest2.R script. Add new shiny tests totest-shinytest2.R, also any tests generated usingshinytest2::record_test()will be automatically added to this file.