Dealing with software complexity in individual‐based models

Individual‐based models are doubly complex: as well as representing complex ecological systems, the software that implements them is complex in itself. Both forms of complexity must be managed to create reliable models. However, the ecological modelling literature to date has focussed almost exclusively on the biological complexity. Here, we discuss methods for containing software complexity. Strategies for containing complexity include avoiding, subdividing, documenting and reviewing it. Computer science has long‐established techniques for all of these strategies. We present some of these techniques and set them in the context of IBM development, giving examples from published models. Techniques for avoiding software complexity are following best practices for coding style, choosing suitable programming languages and file formats and setting up an automated workflow. Complex software systems can be made more tractable by encapsulating individual subsystems. Good documentation needs to take into account the perspectives of scientists, users and developers. Code reviews are an effective way to check for errors, and can be used together with manual or automated unit and integration tests. Ecological modellers can learn from computer scientists how to deal with complex software systems. Many techniques are readily available, but must be disseminated among modellers. There is a need for further work to adapt software development techniques to the requirements of academic research groups and individual‐based modelling.

Although including this complexity is important to increase the predictive power of ecological models (Evans et al., 2013;Stillman et al., 2015), it can also make the models hard to reason about and analyse (Manson et al., 2020). Indeed, it has been argued that increasing complexity is both a necessary development and one of the greatest challenges for the field (Cabral et al., 2017). Following the adage that IBMs 'should be constructed as simple as possible, and as complicated as necessary' (Sun et al., 2016), significant work has been done to develop methodological and analytical techniques for dealing with this biological complexity. These techniques include pattern-oriented modelling (Grimm & Railsback, 2011), 'evaludation'  and cascaded design of simulation experiments (Lorscheid & Meyer, 2016).
However, in its quest to cope with the scientific consequences of model complexity, the ecological modelling community has neglected some of the technical aspects of modelling. IBMs simulate complex ecological systems using computer code (software) that is itself a complex system-in effect, they display a double complexity.
Thus, to generate reliable results, we must not only think about the biological complexity of our models but also about their technical complexity (Grimm & Railsback, 2005;Ropella et al., 2002). This is all the more important because unreliable software can have catastrophic consequences for entire research fields (e.g. Eklund et al., 2016;Hatton, 1997).
However, this is an engineering challenge, not primarily a scientific one, and as such many ecologists may feel uncomfortable tackling it (Cooper & Hsing, 2017;Nowogrodzki, 2019). The problem is not restricted to ecology: a growing realisation of the importance of software to science has led to the emergence of the field of 'Research Software Engineering' (Cohen et al., 2021) and several other groups working to improve the software development skills of scientists. In line with these developments, we authors argue that as ecological modellers, we ought to pay more careful attention to the software running our models.
Previous publications have established best practice in regard to the workflow of model development Stodden & Miguez, 2014), suggested methods for scaling large simulations (Parry, 2009), or given general guidelines for scientific programming (Balaban et al., 2021;Wilson et al., 2014Wilson et al., , 2017. Therefore, in this commentary, we will specifically look at the question of software complexity in IBMs, with the aim of increasing model reliability. In doing so, we will draw on some of the classical software engineering literature, as well as our own experiences and observations in the development of open-source applications and IBMs (e.g. Ankenbrand et al., 2018;Leidinger et al., 2021;Petter et al., 2021). The individual points will be illustrated using references to published IBMs with publicly available source code (Table 2). We intend this overview to be particularly helpful for graduate students and early career researchers who already have some modelling experience, but would profit from a firmer grounding in the 'tools of the trade' of software development.
Right from its inception, the field of computer science has had to deal with the issue of software complexity (Dijkstra, 1972). As programs became larger and 'programming' turned into 'software engineering', numerous techniques were developed for containing this complexity. These techniques have been applied to language design, coding style, software architecture and development workflow (Brooks, 1986). In fact, this topic is considered to be of such great importance that one well-known textbook states that 'reducing complexity is arguably the most important key to being an effective programmer' (McConnell, 2004, p. 839 Multiple development techniques target each of these strategies.
In the following section, we will briefly present a collection of these techniques and give examples for how they have been applied to ecological IBMs. Subsequently, we will discuss steps that could or should be taken to better integrate good software development practices in the field of individual-based modelling.

TA B L E 1
Overview of strategies and techniques for dealing with biological and software complexity in individual-based models. See the main text for explanations and references 2 | PR AC TI C AL RECOMMENDATI ON S

| Writing code
To avoid unnecessary complexity (Table 1), readability must be the first concern when actually writing model code. In the words of Abelson et al. (1996): 'Programs must be written for people to read, and only incidentally for machines to execute' (p. xvii). Although in some special cases other needs may override this rule (e.g. in performance-critical sections), these must remain justified exceptions. Readability is a core aspect of understandability, which makes programs easier to learn, reason about and analyse. Software that is easy to understand is less prone to mistakes, and therefore more reliable (McConnell, 2004).
An observational study of 78 professional software developers showed that only 5% of their time was spent directly editing the code, whereas 58% was spent understanding the code base and a further 24% navigating it (Xia et al., 2018). Importantly, the developers cited comparatively trivial root causes for this high understandability cost.
These included insufficient comments, meaningless variable names, overlong functions and classes, inconsistent coding styles and exaggerated inheritance hierarchies in object-oriented programs.
This highlights the essential nature of 'clean code' (cf. Martin, 2009). Basic practices can make a significant difference, such as dividing code into appropriately sized functions and files, or keeping layouts simple by avoiding long lines or deep nesting. Further guidelines may be found in Wilson et al. (2017), in the style guides published for many programming languages, or (in great depth) in McConnell (2004).

| Choosing a language
The choice of programming language is often both deeply subjective and contingent upon external circumstances such as available collaborators and libraries. Nonetheless, we believe that it is important to choose the language for a new project carefully, as the decision will influence the project over its entire lifetime. Therefore, based on our personal experiences, we will hazard to give an opinion on a few popular programming languages below, as well as a recommendation for our preferred language.
When choosing a language for a model, one does not want to introduce unnecessary complexity with an overly complicated language (Table 1), yet one must consider the computational requirements of large models. Thus, the ideal language for complex IBMs ought to be one that is easy to program in, while offering a good runtime performance. This, however, casts questions on some common modelling languages. Specifically, C++ is generally perceived as fast but very complicated; it is powerful, but error-prone particularly with regard to its manual memory management. R is great for data analysis, and enjoys the advantage of being the only language that many ecologists are taught in their university courses. However, for individual-based models, it is often both slow and linguistically so TA B L E 2 Examples of open-source individual-based models and other ecological software that are referred to in the text.

Illustrates
DEBplant Mechanistic species distribution model Schouten et al. (2020)  NetLogo also can be a good choice, as it is simple to learn and tailored to the needs of individual-based modellers; despite its former reputation for slowness, improvements over the past years now make it a feasible platform even for large models (Railsback et al., 2017).
After having used all of these languages at various times, our working group has settled on Julia (Bezanson et al., 2017) as our current 'ideal candidate'. Julia is a comparatively new language that is explicitly designed for the requirements of computational scientists. Its syntax is similar to Python and easy to pick up, it supports multiple programming paradigms (including object-oriented and functional programming), and has an excellent performance even without specialised libraries (Lubin & Dunning, 2015). Its rapidly growing popularity among computational scientists has greatly increased the availability of libraries and help resources. Individualbased models can be written directly in the language, or make use of the agent-based modelling framework Agents.jl (Datseris et al., 2021). There are also mature libraries available for visualisation, such as Plots.jl (Breloff, 2021) or the aforementioned Agents.
Obviously, shifting to a new language represents a significant time investment, both in terms of learning and teaching as well as porting any existing code. Also, the size of the 'language ecosystem' is often crucial: how many libraries are available, how easy is it to find help, do project collaborators know the language too? Indeed, such community constraints often make modellers hesitant to pick up a new language, particularly as most ecologists are only taught R in their university courses. Nonetheless, it should be pointed out that learning a new language is a one-time investment that pays long dividends and may well save time and effort in the long run. In short, we believe that the Julia ecosystem is now large and stable enough, and the long-term benefits great enough, to recommend the increased

| Working with file formats
File formats are conventions for how to structure files that are read or manipulated by computer programs. They may be specified by official committees (e.g. PDF, HTML), or be defined ad-hoc for individual programs. IBMs often interact with multiple input and output file types, such as GeoTIFF for map inputs or CSV for configuration files. As different file types have different advantages and disadvantages, it is worth thinking about which formats to use and how to create new ones.
Fundamentally, a format may use binary (e.g. PDF) or textbased (e.g. HTML) data encoding. In the programming tradition that grew up around the Unix operating systems (such as Linux or Mac OS), a premium is placed on using textual file formats wherever possible (Raymond, 2003). Although these may require more storage space than binary formats, they have a number of practical advantages that are directly relevant to the development of IBMs.
First, they are human-readable, and can easily be opened with any text editor. This makes debugging a lot simpler, as one is not dependant on special software to analyse model output. Second, text files can be read, analysed and processed by a whole host of other programs and programming languages. This means one can mix and match at will, always using the best tool for the job, instead of being locked into one particular software (see Section 2.2). And third, compared to direct binary 'memory dumps', the level of indirection introduced by exporting to text-based formats automatically encourages encapsulation (see Section 2.4) and helps to keep a model backwards-compatible as it grows. Thus, text-based file formats help to avoid complexity on multiple levels ( Table 1).
Because of this, we decided to use text-based input and output formats for the GeMM model, which allows us for instance to easily script the creation of configuration files for an experiment, or to output FASTA data for analysis with standard bioinformatical tools (Table 2; Leidinger et al., 2021).
In some cases, programs begin to rely so heavily on their custom-made file formats that these develop into what are known as domain-specific languages, or DSL. This may happen when the program requires extensive and intricate configuration, or the developers use the file format to rapidly add new content (such as player items in a computer game, or species in an IBM). This can be very effective, as using a simple language designed specifically for the problem at hand is more efficient than using a complex general-purpose language (Raymond, 2003). It is also a great way to build abstraction layers and subdivide complexity (see Section 2.4; Table 1), as a DSL represents a clearly defined interface between different sections of the software (Abelson et al., 1996).

| Encapsulation
This technique is most important during the software design stage, when constructing the code architecture. Here, the key principle is 'divide and conquer', that is, subdividing a complex system into simpler subsystems (

| Documenting models
To maintain understandability of any non-trivial model, adequate documentation is critical (

| Code reviews
Even well-designed and documented software systems cannot be expected to be free of defects and errors. Therefore, reviewing and testing source code regularly is critical (Table 1). Submitting software to internal reviews has been shown to be one of the single most effective means of increasing code quality. Numerous studies have shown that on average, such reviews can be expected to find ~60% of bugs (Shull et al., 2002). This is even more effective than automated testing, and can save significant debugging time down the line. Also, reviews have the added advantage of rapidly disseminating know-how and best practice within teams (see the discussion in McConnell, 2004).
Reviews may be done very formally, with moderated meetings to inspect a section of code using standardised checklists (Aurum et al., 2002). They may also be more informal, using digital tools to quickly review new contributions to a software (Rigby & Bird, 2013).
Either way, they require regular time investments, and ideally need to be embedded in a team's development routine. However, the benefits are so great that this investment should pay for itself. Indeed, code reviews have already been recommended for use in IBM development (Ropella et al., 2002), and are provided for in the TRACE protocol as one method of implementation verification . To give a practical illustration of how to conduct code reviews, we provide an inspection checklist that we developed for use in our institute (Vedder, 2019).
External code reviews can also be an important supplement to the normal peer-review process for publications. For example, the rOpenSci project has established a code review system for scientific software that interfaces with the publication processes of the

| Code testing
Beyond reviewing the source code of a model, it is however also important to test its actual functioning (Table 1). In software engineering, this aspect of development is often divided into unit testing and integration testing (McConnell, 2004 A long-standing debate surrounds the extent to which such automated unit testing should be implemented. There are vocal proponents of a 'test-driven development' (TDD) approach, who argue that every function and feature in a code base should have an automated test accompanying it, and that this test should be written before the feature itself (e.g. Martin, 2009). As well as creating a comprehensive test suite for automated testing, such test-driven development encourages a more stringent and focussed programming style (Jeffries & Melnik, 2007). However, TDD is very timeconsuming to implement, and in terms of defect-finding, other techniques such as code reviews appear to be more effective (see Section 2.6). Therefore, other authors like McConnell (2004) and Balaban et al. (2021) suggest using automated testing primarily for fundamental or critical functionality.
What must not be neglected in any case is integration testing.
This refers to the testing of the complete software, including all submodules. This is somewhat more difficult to automate fully, although an automated integration pipeline greatly speeds up the process (see Section 2.8). Also, it may be possible to automate the comparison of certain output variables against the values of these variables as produced by previous model versions. Another classical technique of integration testing for IBMs is output visualisation (Ropella et al., 2002). Having the model display maps of its various entities and graphs of key metrics (such as population size or species number) will rapidly show patterns that run counter to or align with theoretical expectations (Grimm & Railsback, 2011).
Additionally, one can use logging facilities (see Section 2.5) or dedicated tools known as debuggers to inspect the state of internal model objects during its execution. These latter techniques are commonly used during debugging, that is, when searching for the root cause of a known defect. However, they can also be used proactively to investigate whether the software's internal working conforms to expectations, and thus to find bugs that may not be immediately visible in the model output. For more details, see the discussion on testing in (Grimm & Railsback, 2005, ch. 8.5).

| Automated integration
In the context of software development, integration refers to the combining of individual components into a larger system, and the subsequent testing of this system (McConnell, 2004). This is a regular procedure in an IBM workflow, where models do not just have multiple components in and of themselves, but may also have auxiliary software to set up and analyse experiments (see Section 2.2).
This process should be automated as much as possible, to avoid unnecessary complexity ( we have three scripts to cover the complete modelling pipeline. The first script takes GeoTIFF files and converts them into the model's text-based landscape input format (see Section 2.3). The second script generates the set of configuration files needed for an experiment and launches the simulation runs. Finally, the third script reads in all output files and performs data analyses and visualisation.
In the software industry, this automation of the deployment pipeline has become known as continuous integration (CI, see the discussion in Brown & Wilson, 2011). It allows a faster development cycle, due to the smaller time gap between code development and execution. Also, adding automated testing into this pipeline means that bugs are discovered sooner rather than later, increasing developer confidence in their code (Hilton et al., 2016). All major git hosting platforms now support CI so that automated tests can be set up to run regularly, such as when merging a new feature branch.
Considering IBMs, application of CI principles should furthermore reduce the usage complexity of models and help establish a greater reproducibility of results.

| NE X T S TEPS
Having recognised the problem of double complexity inherent in IBMs, where should we go from here? As authors, we believe that we need a thorough discussion in the ecological modelling community about which complexity-containing techniques are applicable and feasible for our model software. This paper aims to continue and promote this discussion. Hopefully, the next few years will see more of these techniques integrated into our workflows, and explained in our textbooks.
Encouragingly, the field of climate modelling has already been through this very process. For example, Easterbrook and Johns (2009)  This is generally only feasible for larger groups, as salaries in the IT industry are very competitive and often beyond the budget of most smaller research groups. This is especially problematic as many universities and funding agencies are still reluctant to pay for such positions (Nowogrodzki, 2019). Hence, although the approach of hiring professionals should be encouraged, training ecologists in computational skills will remain important for the foreseeable future.
Fortunately, the wider scientific community is becoming increasingly aware of the importance of good software development skills.
The Software Carpentry project is a volunteer-based initiative offering basic 2-day software training courses for scientists (Wilson, 2016). Similarly, the rOpenSci community provides programming guidance and teaching, as well as building and maintaining a curated collection of software tools for ecological and evolutionary research (Boettiger et al., 2015). Complementing this, the concept of Research Software Engineering represents a push to create highquality scientific software through close collaborations of scientists with professional software developers (Cohen et al., 2021).
As ecological modellers, we can profit from all of these initiatives. As a field, we need to cultivate a greater software engineering know-how, but this is not impossible. There is an extensive literature available on the topic, we can learn from colleagues in other departments, and, if funds permit, we can hire professionals to help us.
Most importantly, we need to share experiences and develop a set of tried-and-tested best practices for our field.

| CON CLUS IONS
In this commentary, we have argued that we need to think about both biological and technical complexities if we want to get reliable results from our individual-based models. Although a lot of work has been done on biological model complexity, little has been written about the aspect of technical complexity. Fortunately, computer science has decades of experience in dealing with software complexity that we can learn from.
Key strategies are to avoid, subdivide, document and review complexity. Techniques to do so include writing clean code, choosing suitable languages and file formats, encapsulating submodules and abstraction layers, and documenting both biological and technical details. Additionally, code reviews and unit and integration tests are needed to verify code quality. Automated integration can speed up the modelling workflow and decreases the likelihood of procedural mistakes.
As IBMs continue to grow in scope and importance, learning to cope with their double complexity becomes increasingly vital. This paper provides some pointers, but we need more cross-pollination from the computer sciences, and a more thorough methodological discussion in the modelling community.

ACK N OWLED G EM ENTS
We are grateful to our colleagues at the CCTB for the years we have spent working and learning together as computational biologists.
Thank you for your camaraderie! We also appreciate the comments by Bob O'Hara and two anonymous reviewers, which much improved this manuscript. M.A. and J.S.C. acknowledge funding from the 'Zentrum für digitales Experimentieren 4.0' (ESF-ZDEX). J.S.C.
further acknowledges funding from the Bavarian Ministry of Science and the Arts in the context of the Bavarian Climate Research Network (bayklif). Open Access funding enabled and organized by Projekt DEAL.

CO N FLI C T O F I NTE R E S T
The authors declare no conflict of interests.

PE E R R E V I E W
The peer review history for this article is available at https://publo ns.com/publo n/10.1111/2041-210X.13716.

DATA AVA I L A B I L I T Y S TAT E M E N T
The sample code inspection checklist may be downloaded at https:// doi.org/10.5281/zenodo.5284378 (Vedder, 2019).