A call for clean code to effectively communicate science

Effective coding is fundamental to the study of biology. Computation underpins most research, and reproducible science can be promoted through clean coding practices. Clean coding is crafting code design, syntax and nomenclature in a manner that maximizes the potential to communicate its intent with other scientists. However, computational biologists are not software engineers, and many of our coding practices have developed ad hoc without formal training, often creating difficult‐to‐read code for others. Hard‐to‐understand code can thus be limiting our efficiency and ability to communicate as scientists with one another. The purpose of this paper is to provide a primer on some of the practices associated with crafting clean code by synthesizing a transformative text in software engineering along with recent articles on coding practices in computational biology. We review past recommendations to provide a series of best practices that transform coding into a human‐accessible form of communication. Three common themes shared in this synthesis are the following: (a) code has value and you are responsible for its organization to enable clear communication, (b) use a formatting style to guide writing code that is easily understandable and consistent and (c) apply abstraction to emphasize important elements and declutter. While many of the provided practices and recommendations were developed with computational biologists in mind, we believe there is wider applicability to any biologist undertaking work in data management or statistical analyses. Clean code is thus a crucial step forward in resolving some of the crisis in reproducibility for science.


| INTRODUC TI ON
Coding is increasingly integrated into the biological sciences. There is the argument that all biology is computational biology, where contemporary research has long been dependent on computers (Markowetz, 2017;Wilson et al., 2017). Code and software can thus support many dimensions of scientific analysis in biology through data management and analysis (Hampton et al., 2013;Marx, 2013;Nussinov et al., 2015), but there is still a need to discuss and examine general practices. We use computation to manage and interact with the biology that we study to varying extents (Del Ser et al., 2019;Gardner, 2019;Oudeyer, 2018). For some studies, such as those that model species responses to climate change (Barton et al., 2019;e.g. Elith & Leathwick, 2009;Pollock et al., 2014), computation forms the entirety of the study. We propose here that computation used in science is a form of communication and evidence. This is similar to how an ecologist can share field notes, photographs or descriptions of methods in journals that focus on methods and approaches.
However, as with other forms of communication, the ability to understand information is central to knowledge development. Biologists often spend considerable effort in writing clear, understandable methods sections for experiments, but we should consider applying that same to our computational methods.
There is a need for clean code that we define here as the code that is easily communicated to and understood by other humans (Martin, 2009;Wickham & Grolemund, 2016). The increasing use of code in ecology and evolution comes at a time when there is also a reproducibility crisis defined as the inability to replicate previous findings through experimentation or analysis (Baker, 2016;Kelly, 2019;Maxwell et al., 2015). Although there are limited incentives for greater reproducibility (Fraser et al., 2020;Kelly, 2019) and some question what is truly reproducible (e.g. Filazzola & Cahill Jr, 2021), having code and data that are computationally reproducible (i.e. Hampton et al., 2017) is within our agency as biologists. There have been increased calls to share code and data with published data descriptions and analytical manuscripts (Powers & Hampton, 2019;Ram, 2013). This coincides with an increasing percentage of biologists using scripting languages, such as R or Python, for their research projects (Bassi, 2007;Lai et al., 2019). Publishing one's code is a step forward, but the ability to understand another's script is open for improvement. While there is an initial cost of time involved with writing clean code, the person most likely to experience a benefit is a future version of ourselves, returning to the project months or even years later. This later benefit will often be time saved, not having to struggle to understand prior intentions.
We propose that additional focus can be applied to producing clean code for the biological sciences.

| WHY CLE AN CODE?
The ability to understand code is just as important as its ability to complete a task. It is important to do both because society has seen the widespread adoption of microprocessors in devices from cars to fridges to light switches, where the incorrect placement of a comma can cost time, money or lives (Martin, 2018). In an extreme case, the dual crashes of Boeing 737 Max-8s that cost hundreds of lives were attributed to a failure in the MCAS software controlling the plane's pitch (Johnston & Harris, 2019;Travis, 2019). As computational biologists, our responsibility is equally important because our research can go on to inform decisions such as medical treatments, ecological restoration and agricultural practices (Dobrow et al., 2004;Olden et al., 2014;Swinton et al., 2007). Understandable code can decrease the likelihood of errors or bugs and will certainly increase the ability to find errors upon review. Clean code is thus linked to the integrity of our science.
Clean coding can increase accessibility because well-written code can let collaborators reuse shared scripts or review them to facilitate communicating analytical methods. We frequently share or obtain chunks of code from online sources, such as Stack Overflow (Gómez et al., 2013;Rosen & Shihab, 2016). The easier that chunk is to understand, the more useable and reliable it will be. There are also differences in coding backgrounds between team members with some favouring certain libraries or packages. Clean code can also communicate the overall purpose and analytical steps regardless of the programming language being used. Writing clean code can thus open the black box of analyses among collaborators, facilitating our ability to produce expedient and reproducible results. There is an opportunity to describe high-level rules from computer science disciplines (Tariq et al., 2020) that can increase readability for computational biologists.

| THE BA S I C S OF CLE AN CODE
Computer science has worked to address the challenge of producing 'clean code'. There have been significant efforts to create a set of best practices for crafting code. One notable example comes from Robert C. Martin, a software engineer who has published five books on best coding practices. The first text, Clean Code: A Handbook of Agile Software Craftsmanship (Martin, 2009) is an excellent starting point for computational biologists. Although the book is written for Java, most practices are broadly applicable to computational biologists. We describe three themes in the Clean Code that are relevant for improving the readability of code: communication, formatting and abstraction.

| Communication
Code is a form of communication. Clean code will communicate the tasks being conducted without the need for comments, methods or supplemental sections. Code comments are annotations within code for the reader, not the machine, that are meant to provide additional information or clarity to what is being executed. There is certainly a reliance on comments in computational biology to communicate the intention of code and some packages have been designed to automate the process of annotation (e.g. Verde Arregoitia, 2022).
Detailing code actions in a comment may start with good intentions, but later this can become problematic as there is a substantial cost in maintaining (or failing to maintain) these comments (Vogel, 2013).
Updates to comments are sometimes neglected, and remnant comments remain that disagree with the code. Other comments include code that has been commented out, remaining as a zombie in the script out of fear that deletion will remove something important that was long forgotten. There are many instances when the information in comments could be better represented inside the code itself. Relying on the code to communicate is often the best approach by (a) renaming functions to describe their operations, (b) removing comments that repeat arguments, (c) removing commented-out code unless it is serving a purpose (Figure 1; Supplemental S1). The choice of names is also important, with style guides recommending functions be verbs and objects be nouns (e.g. function = compare-PlantGrowth vs. object = plantGrowth) (Van Rossum et al., 2001;Wickham et al., 2019). Style guides for Python commonly recommended that objects with multiple items be pluralized (e.g. list = cars) (Van Rossum et al., 2001). Many computational biologists may aim for brevity in naming objects or functions, but a descriptive name will be more informative and will ultimately save time as it replaces the need for comments (e.g. Figure 1). Additionally, many integrative development environments (IDEs), such as VS Code, RStudio or PyCharm, support autocompletion reducing the need for short names. Designing the code itself to communicate information, and using comments sparingly for additional clarity, is often a direct way to impart information to other users.
The file structure and the order of code chunks can be as important to readability as the content of the code. More commonly referred to as software architecture, code organization includes creating an intuitive project design with predictable file structures, compartmentalizing functions, component separation and data management . This is a vast topic that goes beyond the scope of our manuscript. However, there are some aspects of code organization relevant to clean crafting in ecology and evolution. For instance, the use of header blocks or subheaders (i.e. sections of multi-lined comments) in code can be useful for identifying important breaks in the script. For a researcher, the header identifying the code chunks associated with specific predictions from their manuscript can facilitate connecting the analysis to the results. A header at the beginning of a script can also provide notes to the user for (re)use, warnings, required parameters or future development plants. Brevity should remain the goal and caution to avoid repeating anything that is already represented in the code. These headers are also not meant to be replacements for proper documentation, such as a formally deployed package, guide or vignette.

| Formatting
Coding requires many micro-decisions about formatting. These micro-decisions include the placement of whitespace, how to name F I G U R E 1 An example in R of frequently annotated code that can alternatively be conveyed within the code instead. In the revised chunk, many of the comments are removed since the functions capture the same information. The normality test that is commented out is removed, which can be returned if needed and currently serves no purpose. Lastly, the name of the function captures the purpose of the chunk and thus the first comment explaining the chunk is no longer necessary. objects or functions and choice of functions, among many other formatting decisions. We provide nine formatting considerations when coding to help improve the cleanliness of code (Table 1). These formatting considerations can be generally summarized by providing adequate spacing, grouping like things together, and aiming for consistency. Spacing is a powerful tool in coding to separate chunks (paragraphs), lines (sentences) or arguments/objects (words), like the written word in most languages. Ideally, whitespace across lines, chunks, operators and words should be used strategically to indicate related and unrelatedness (dos Santos & Gerosa, 2018). In a sample script separating comma separate files, we demonstrate that spacing can improve the ease with which arguments are identified and our ability to understand the actions (Figure 2; Supplemental S1). Some individuals will advocate for certain formatting styles over others.
For example, tidyverse in R recommends the use of snake_case as a naming convention . We argue the best practices to use are the ones shared by the group you are working with.
There are tools available to support the consistent formatting of code. Standardized style guides can be an effective strategy for guiding code structure, including when to use code chunks, pipe operators and functions (Wickham, 2019). For those looking to take advantage of existing style guides, there are popular examples in many programming languages that can be modified, such as from Google or Mozilla (google.github.io/style guide/, firef ox-sourc edocs.mozil la.org/code-quali ty/codin g-style/ index.html). A similar guide already exists in ecology and evolution produced by the British Ecological Society (e.g. Cooper, 2017). There are popular extensions available in IDEs that can assist with real-time coding to produce clean code (Saini & Mussbacher, 2021). For example, RStudio has recently adopted a vertical line in the source code editor to indicate an 80-character line limit. Code formatters and linters can be used to enforce consistency by flagging stylistic errors or bugs. Prettier and Intellisense-based extensions in VS Code are popular among many programing languages that allow custom inputs for stylistic choices including formatting files on save, decisions on whitespace usage or returns after loops and functions (often called linters). Similar tools also exist for R (e.g. https://lintr.r-lib.org/, https://styler.r-lib.org/), Python (https://black.readt hedocs.io/, flake8.pycqa.org) and Julia (https://domlu na.github.io/Julia Forma tter.jl). In addition, there are other extensions that support auto-completion of entire chunks of code including 'snippet' libraries or Github Co-pilot (https:// github.com/featu res/copil ot/), which can support consistency between scripts and even developers. It is worth noting that these tools are not complete replacements for understanding appropriate TA B L E 1 Common best practices and guidelines for formatting code in biology. Some of these practices are language dependent and must be adjusted accordingly Another element of formatting is the vertical and horizontal ordering of code (Table 1). Vertical ordering includes how lines are presented in the script and often has a formulaic approach.
Computational biologists will often approach this formula sequen- To assist in tracking dependencies within projects and help create succinct scripts, users can turn to workflow management packages such as targets or scipiper in R (Appling, 2020;Landau, 2021), SnakeMake in Python (Mölder et al., 2021) and GNU-Make in Unix (Baker, 2020). Horizontal ordering is crucial for pipe-style function chaining. Restricting lines to 80-120 characters is recommended (Martin, 2009) and building code through incremental layers not only improves the ability to understand code but also allows opportunities to break lines (Wilkinson, 2012). It is important to note that order matters and that functions which add rows or columns should be performed before functions that collapse or summarize.
Clean code typically also omits argument names within functions that are common or routine (i.e. defaults are set to the most common parameters). These recommended best practices keep code tidy through consistent ordering, a readable flow of scientific reasoning (Grolemund, 2014), a limited need for commenting rationales and mechanisms to reduce repetitive lines of code. These salient principles support high-level heuristics for literate coding in many programming languages.

| Abstraction
Abstraction in computer programming involves removing complex elements to increase focus and emphasis on what is important to the user (Shaw, 1984). In computational biology, this would involve extracting a series of functions from a single code chunk to make obvious what is occurring. To better describe abstraction, we present three code chunks of data preparation before running a logistic regression (Figures 3 and 4; Supplemental S1). In this example, the underlying code is identical across the three code chunks, where the observations of a species are loaded to extract climate values, cleaned to remove duplicates or missing values, checked for collinearity issues, and then a logistic regression is conducted (Figure 3).
The difference among these chunks is the presentation of the code, where specific operations are wrapped into functions that are loaded elsewhere in the script. While often this can increase the quantity of F I G U R E 2 A Python script using nested for loops to separate species from the main tab-separated file into individual comma-separated files. In the original script, there is no use of white space to delineate similar items, shorten lines or separate code chunks. The revised script ends up being longer, but the arguments are more apparent.
code written, it certainly can increase the focus of the reader on tasks that are being executed. For future reviewers or users of this script, the most abstracted example makes abundantly clear the purpose of this code and provides brevity in the actionable lines of code. We recognize this is likely one of the more controversial topics in cleaning code. As scientists, we strive for full transparency and reproducibility (Baker, 2016;Fraser et al., 2020). Abstraction may be viewed as a strategy to obfuscate the underlying operations of code by packaging up details in functions that exist elsewhere in the script or another script entirely. Alternately, when used inappropriately, abstraction can lead to unnecessary complexity. Abstraction should break down complex code chunks into smaller, self-explanatory tasks to better describe the purpose or the script. Chunks of code that share similar roles, such as the data cleaning steps in Figure 4, can be nested into a single function, and those functions nested into a larger function (i.e. higher-order function) that has a more complex outcome (e.g. conduct a linear model). We argue abstraction significantly increases the cleanliness of the code and that those details still exist when needed for someone reviewing the project.
Applying abstraction is as much of an art as it is a science. The coding languages we often use in computational biology already have an inherent level of abstraction being high-level dynamic languages (e.g. Julia, R, Python). Consequently, it can be difficult to determine when abstraction should be applied to create a new function or dedicated class. We recommend the following three elements when applying abstraction to a project: map, stepdown and simplify. First, it is important to map an entire script, similar to outlining a manuscript before writing the text. No specifics are needed at this stage, simply the identification of code chunks that will answer project objectives. Second, with the completed outline we can apply the stepdown rule so that high-level functions are run first with low-level functions executed within the function (Martin, 2009).
This approach does require some practice to learn because involves treating actions within the script as going from high-to-low levels rather than sequentially from top-to-bottom. Lastly, one should aim to simplify certain chunks as much as possible. Simplification may be accomplished by bundling integrated processes into functions (i.e. Figure 3), but this process can also be achieved by transforming repetitive steps into a function. However, it is notable that this effort at improving clarity can, at times, come at a cost to efficiency. For example, while for loops tend to be difficult to read relative to map functions, the loops can be faster (see Crites, 2018). Using these three steps (map, stepdown and simplify), we can apply abstraction to increase the readability of our code.

| PRIN CIPLE S OF CLE AN COD ING
Computer science has developed a suite of best practices to use when crafting code each with varying applicability for the computational biologist. Some are more consistently relevant that are worth mentioning for further consideration by readers when crafting code. For instance, the DRY principle (do not repeat yourself) is an important axiom to minimize code repetition that can certainly facilitate readability (Thomas & Hunt, 2019). There are also the multiple principles that have been popularized in objectoriented and functional coding languages. For example, the singleresponsibility principle, where a function should only conduct a single task (Martin et al., 2003). In our provided example (Figure 3) wrapping the entire original chunk into a single function would violate this principle without first breaking each action into its own set of functions (i.e. the first level of abstraction). This approach F I G U R E 3 An outline of the abstraction steps taken in Figure 4 to replace chunks of code with descriptive functions. Lines of code can be grouped based on similar actions and then nested into functions. Abstraction will typically increase the total lines of code overall because additional lines are needed to include the syntax for user-defined functions. However, the number of actional code items that require user inputs or edits will decrease substantially.
typical code first draft F I G U R E 4 Three levels of abstraction to conduct a logistic regression using climate data extracted from survey locations of a target species. In the first level of abstraction, sections of the code chunk are wrapped into functions to improve clarity. For instance, the data cleaning steps are wrapped into a data cleaning function. In the second level of abstraction, one can wrap the entire chunk into a single function (runClimateLogisticRegression) offering the reader the opportunity to focus more on the other elements of data preparation for the subsequent analysis, data predictions or visualizations.
improves readability and minimizes obscurity by keeping functions simple to understand. Jenny Bryan in a UseR plenary provided an excellent set of tips to improve the way 'Code smells and feels', such as minimizing indentation, revising nested if-else statements into separate functions when possible, and avoiding complicated Boolean expressions (Bryan, 2018).
Team composition is an important factor in determining the readability of code. The structure, diversity of experience and skill set of team members will lead to different adopted practices.
The best formatting style guide is the one shared by team members, but there are other challenges for team-based coding. One significant challenge can be relative differences in the skill set.
Relatively more novice individuals may focus more on immediate functionality rather than higher-level repeatability or readability.
In computer science, there is the expression 'make it work, make it right' (Beck, 2003) that certainly also applies to computation in ecology and evolution. First, get the code working. Second, spend the time improving the code as a team to ensure repeatability and readability by others. Different team members, for instance, initially comment more often to provide explanations for what arguments certain functions require, eventually transitioning to removing these comments. The technique for improving the design of preexisting code is called refactoring, which is a common practice in computer science (Fowler, 2018). Any return visits to code should likely include design improvements, regardless of how small the change, to improve future use and inevitably lead to substantial change (Fowler, 2018). Clean code is thus not a binary (i.e. clean vs. not clean) designation, especially in collaborator work. Instead, clean code is a continuum that includes flexibility in shared practices adopted by the team, so as not to exclude junior members and respect the differences in the need to annotate functions or steps.

| IMPLIC ATIONS
Consistency between the structure and semantics of data can provide benefits beyond legibility, particularly facilitating clean thinking in project design. The incredible flexibility of interpreted programming languages is that there are often many solutions to a single challenge including data munging (Dasu & Johnson, 2003). Nonetheless, a set of practices and preferred formatting reduce the cognitive costs of mentally manipulating representations from data structures to code to more extended workflows including functions and code chunks or modules (Menary & Kirchhoff, 2014). Applications of clean code can thus result in relatively consistent formats thereby lowering the mental overhead for computational biologists (Al-Fedaghi, 2021; Codd, 1990). This seamlessness can be further enhanced by style guides or generalized cultural practices within a community of programmers, although these practices vary between groups. Some packages and libraries ensure code conforms to the style guides and enables modularized code reuse. Adopting style guides increases consistency in coding through shared styles that are clean and/or tidy for instance lowers the number of decisions needed for the programmers that are not directly associated with the purpose of the work and further reduces the likelihood of mistakes.
We recognize that clean code is not a silver bullet in solving computational reproducibility, but rather one of many skills for improving the transparency of our science. For example, avoiding complexity in software or analytics through subdivision, documentation and review can make our code inherently more understandable (Vedder et al., 2021). The increasing popularity and awareness of the '10 Simple Rules' series also provides multiple great recommendations for improving computational reproducibility such as using version control, providing ancillary documentation (e.g. README files, worked examples, vignettes) and automation (Lee, 2018;Sandve et al., 2013;Wilson et al., 2017). Version control, such as with Git, can be especially powerful in providing a history of code changes, reducing the need to preserve old or unused code. However, the benefits from each of these recommendations would be best realized when writing clean code. While most biologists did not get into the discipline to become computer programmers, there are certainly some 'good enough' practices to enable reuse, replication and understanding (Wilson et al., 2017). Among the list of good enough practices is the common theme of sharing code and collaboration (Wilson et al., 2017), which can only be achieved if the code is understandable.
We synthesized recommendations from Clean Code (Martin, 2009)  We advocate these recommendations are applied at project conception to facilitate project success and encourage the publication of code. Clean code improves computational reproducibility, a requirement for many journals, but also a significant benefit for our future selves. There is also a broader societal benefit as there is increased transparency and computational reproducibility among biologists.

AUTH O R CO NTR I B UTI O N S
Both authors contributed equally to the conception, design and example creation. Alessandro Filazzola led the writing of the manuscript. Both authors contributed critically to the drafts and gave final approval for publication.

ACK N OWLED G EM ENTS
We thank Sophie Breitbart, Matthew R. Brousil, James Cahill Jr., Michael F. Meyer, Courtney Robichaud and two anonymous reviewers for their comments on an earlier draft and suggestions for general coding practices. We also thank Robert Martin for his comments relating clean code to the R Users MeetUp group. That presentation and discussion inspired this perspective. This research was funded by a postdoctoral fellowship awarded to A.F. by the Center for Urban Environments and School of Cities at the University of Toronto, Canada, and an NSERC DG to C.J.L.

CO N FLI C T O F I NTE R E S T
None of the authors have a conflict of interest to declare.

PEER R E V I E W
The peer review history for this article is available at https://publons.com/publon/10.1111/2041-210X.13961.

DATA AVA I L A B I L I T Y S TAT E M E N T
No data were associated with this manuscript. Code snippets can be found online at the following repository https://doi.org/10.5281/ zenodo.6917279 (Filazzola, 2022).