Full text
Audax Development Research Notes - 1 / 2025-11-23 Quantifying Architectural Complexity in Modern PHP Frameworks Andrea Davanzo The Open University, Milton Keynes, UK - https://orcid.org/0009-0000-5170-1737 Abstract Software frameworks are intended to accelerate development by providing pre-built structures and abstractions. However, this convenience is not without cost: these structures introduce a measurable and often excessive structural overhead. This paper introduces the Accidental Complexity Score (ACS), a novel and reproducible metric designed to quantify this precise architectural burden. ACS operates by measuring the volume of primary structural components such as classes, interfaces, traits, and user-defined functions, that must be loaded during the minimal execution of an application. Using a minimal "Hello World" implementation, six popular PHP frameworks are evaluated to isolate and compare their intrinsic structural complexity. The results reveal substantial variation: lightweight frameworks maintain minimal overhead, while heavily structured architectures exhibit a high structural cost that demonstrably correlates with increased memory consumption and execution time. The ACS is framed as a clear measure of Architectural Complexity (AC) relative to an Optimal Algorithmic Implementation (OAI). These findings provide developers and system architects with empirical data allowing them to make informed trade-offs between architectural richness and essential performance efficiency while selecting a PHP framework. This work establishes a foundation for future research aimed at minimizing structural overhead in software systems. Introduction Software architecture is fundamentally concerned with managing complexity. This complexity can be broadly categorized into two types: essential complexity, which is inherent to the problem domain, and accidental complexity, which is introduced by the tools, frameworks, and design patterns used to implement the solution. Modern web development practices often favour sophisticated structures, such as those found in PHP frameworks, that are intended to maximize development, maintainability and testability. However, these frameworks impose a substantial structural overhead even for the most trivial tasks. This paper proposes that the most direct way to assess this accidental structural complexity is by measuring the quantity of items, like files, classes, interfaces, and functions that are required by a framework before any business logic is added. This structural burden must be measured against the simplest optimized version of the solution to the original problem. Page 1 of 11
Audax Development Research Notes - 1 / 2025-11-23 Literature Review Brooks' influential 1987 paper, "No Silver Bullet: Essence and Accidents of Software Engineering", established the fundamental distinction that remains valuable today: essential complexity and accidental complexity (Brooks, 1987). Essential complexity is the difficulty of the problem e.g., the complex business logic of a financial system, while accidental complexity is the difficulty introduced by the tools, languages, and methodologies used for implementation. Brooks argued that most historical advances in software engineering e.g., high-level languages, Object-Oriented Programming, were effective at attacking and eliminating accidental complexity, making the task of building software easier. When Brooks introduced the essential versus accidental complexity distinction, he was formalizing a theme that appeared repeatedly between 1968 and 1978 literature. It was at the NATO Software Engineering Conference, held in Garmisch, Germany, in 1968 that the term "software crisis" was formally used and recognized that availability of powerful computers led to increasingly complex and unreliable software systems (Apt, 2022). Edsger W. Dijkstra, a pioneering figure in computer science, expressed his concerns of producing correct software: “programming has arisen not as a science but as a craft… guided more by opportunism than by sound principles.” Dijkstra (1962) Dijkstra criticizes how numerous tricks invented by developers have ended in a "chaotic contribution" in the name of the efficiency. Furthermore, Wirth (1971) emphasized how unnecessary complications can be avoided by that careful design and stepwise refinement, warning that even correct programs could be poor if design decisions were not revisited critically. Hence, the idea of quantifying accidental complexity via metrics emerges naturally from these problems. If revisiting decisions is important, it is necessary to define ways to measure whether a design is good or bad, rather than relying solely on subjective judgment. The efforts in software metrics started appearing around the late 1960s-1970s, often inspired by exactly this kind of thinking. For example, Halstead (1977) proposed metrics based on operators and operands to estimate program complexity, effort, and maintainability. Later on, the Cyclomatic Complexity introduced by McCabe (1976), aims to measure the number of linearly independent paths in a program to highlight controlflow complexity and design fragility. Metrics have been also in different programming paradigms. In case of Object Oriented Programming (OOP). Chidamber & Kemerer (1994) proposed metrics such as Weighted Methods per Class, Depth of Inheritance Tree, and Coupling Between Objects, focusing on maintainability, modularity, and reusability in modern class-based frameworks. Martin (1994) warned how his Object Orient Design Quality Metrics metrics described in his paper measure are subjective in contrast to the critical review requirements expressed by Wirth. Another factor that should be considered when using software metrics is the context where software is used. In a desktop application the software is read and executed once per session, whereas in a web application the same code is executed for every single request, across thousands of concurrent users. Therefore, the structural cost accepted in exchange for architectural "quality", the core tradeoff mentioned, is amplified by the user load. Page 2 of 11
Audax Development Research Notes - 1 / 2025-11-23 Despite the valuable contributions of established metrics, a significant deficiency remains: none of these methodologies adequately address the volume of structural components loaded and utilised by a software at runtime. Therefore, to quantify this architectural burden, a new metric is needed that specifically tracks the actual component load of the framework core. Methodology Frameworks and web page setup The experiment has been structured as a comparative analysis of minimal web application overhead across six popular PHP frameworks: CodeIgniter 4, Fat-Free Framework, Laminas, Laravel, Symfony, Yii. All the frameworks have been downloaded using the following commands •composer create-project codeigniter4/appstarter ci4 •composer create-project bcosca/fatfree fat-free •composer create-project laminas/laminas-mvc-skeleton laminas •composer create-project --prefer-dist laravel/laravel laravel •composer create-project symfony/skeleton symfony •composer create-project --prefer-dist yiisoft/yii2-app-basic yii The frameworks have been configured in "production mode" in order to avoid the loading of extra elements generally used in "development mode". No other optimisation has been performed. A minimal test case has been implemented which required each framework to perform its essential functions: load its core bootstrap files, execute its routing mechanism, and render a simple template that prints a single string "Hello World". This approach isolates the measurement to the structural overhead and initial execution cost, minimizing variability from a complex business logic. The cesp_log() function To capture the essential metrics required for architectural complexity assessment, a custom PHP function cesp_log() has been developed (Davanzo, 2025). The function is designed to provide a comprehensive snapshot of two critical factors at defined points in the software execution: the resource expenditure and the volume of components introduced by the framework. The function accepts an $action parameter which could assume the following values: •'start': collect and store the data at the start of the execution •'end': collect and store the data at the end of the execution •'print': print out the full JSON record of the data collected For the resource expenditure the functions collect the following information: •memory_usage_start, memory_usage_end, memory_usage_delta: Returns the amount of memory used to PHP at point 'start', ' end and the difference; Page 3 of 11
Audax Development Research Notes - 1 / 2025-11-23 •memory_allocated_start,memory_allocated_end, memory_allocated_delta: Returns the amount of memory allocated to PHP at point 'start', ' end and the difference; •memory_peak_start, memory_peak_end: Returns the peak of memory allocated by PHP at point 'start' and ' end; •memory_real_peak_start, memory_real_peak_end: Returns the peak of memory allocated by PHP at point 'start' and ' end; •microtime_start, microtime_end, microtime_delta: Return current Unix timestamp with microseconds at point 'start', ' end and the difference; For the volume of components introduced by the framework, cesp_log collect and returns the following information: •num_included_files: Number of included files •num_declared_classes Number of declared classes •num_declared_interfaces: number of declared interfaces •num_declared_traits: number of declared traits •num_defined_functions: number of defined functions •num_defined_constants: number of defined constants The cesp_log('start') has been called at the very beginning of the application bootstrap (lines 3 and 4 Figure 1) and cesp_log('end') just before output generation (line 18 Figure 1). This provides a precise measurement interval for the framework's loading overhead. The result has been rendered by calling cesp_log('print') at the end of the process (lines 19, 20, 21 Figure 1). Page 4 of 11
Audax Development Research Notes - 1 / 2025-11-23 Test and data collection The data collection has been automated using a Bash script executed on a dedicated, isolated virtual private server with Ubuntu 22.04.5 LTS, PHP 8.1.2-1ubuntu2.22, and Apache/2.4.52 (Ubuntu) ensuring consistency in hardware and operating conditions. The Bash script uses cURL to fetch the target URL, extracting the JSON between the delimiters. The data collection script includes a Conditional Service Restart procedure. The optional --restart flag in the Bash script allows for the restart of both php8.1-fpm.service and apache2.service before processing. This is vital because PHP-FPM and PHP's Opcode Cache introduce a performance optimization known as a "warm start," where files are compiled and cached in memory after the first execution. Restarting the services ensures a definitive "cold start" for each test, providing the true structural cost of loading the framework from disk. Results The Table 1 summarizes the accidental complexity in terms of volume of components introduced by each framework, for the minimal "Hello World" application. Page 5 of 11 Figure 1: Use of cesp_log() function in Yii bootstrap file
Audax Development Research Notes - 1 / 2025-11-23 Metric CodeIgniter Fat-Free Laminas Laravel Symfony Yii Total Included Files 119 10 206 431 222 62 Declared Classes 84 11 145 226 135 40 Declared Interfaces 14 0 48 70 59 5 Declared Traits 6 0 5 79 11 0 Defined Functions 70 2 3 167 68 10 Defined Constants 32 0 0 14 0 9 Table 1: Accidental complexity in terms of volume of components for each PHP framework Based on this raw data, the structural cost for the minimal task is highly varied. Fat-Free framework shows the lowest structural burden across the board with only 11 declared classes and 2 defined functions. This seems to be aligned with core philosophy behind the framework of minimalism in structural components avoiding application complexity (F3::Community, 2025). Yii is the only one that includes less then 100 files but with a number of classes four times bigger than Fat-free. CodeIgniter instead requires about twice the number of components of Yii but an half of Laminas and Symfony. Both exhibit a similar large number of components, in terms of Interfaces and Classes, however there is a substantial difference in terms of Traits and Functions. Laravel shows the highest number of structural elements. The Table 2 below summarizes the resources utilisation for each framework. Metric CodeIgniter Fat-Free Laminas Laravel Symfony Yii Memory Usage Start (Bytes) 353,824 354,336 361,216 353,840 367,816 352,352 Memory Usage End (Bytes) 1,108,208 467,952 1,406,424 4,198,608 1,472,104 1,067,456 Memory Usage Delta (Bytes) 754,384 113,616 1,045,208 3,844,768 1,104,288 715,104 Memory Allocated Start (Bytes) 2,097,152 2,097,152 2,097,152 2,097,152 2,097,152 2,097,152 Memory Allocated End (Bytes) 2,097,152 4,194,304 4,194,304 8,388,608 4,194,304 4,194,304 Memory Allocated Delta (Bytes) 0 2,097,152 2,097,152 6,291,456 2,097,152 2,097,152 Memory Peak Start (Bytes) 446,232 446,744 453,624 446,248 1,506,080 444,760 Page 6 of 11
Audax Development Research Notes - 1 / 2025-11-23 Memory Peak End (Bytes) 1,515,288 2,132,792 1,623,152 4,514,280 2,172,384 1,501,616 Real Peak Memory Start (Bytes) 2,097,152 2,097,152 2,097,152 2,097,152 2,097,152 2,097,152 Real Peak Memory End (Bytes) 2,097,152 4,194,304 4,194,304 8,388,608 4,194,304 4,194,304 Execution Time Delta (s) 0.1645 0.0188 0.1883 0.4674 0.1736 0.0789 Table 2: Resources utilisation for each framework The Execution Time Delta (s) measures how quickly the framework completes the given task. Lower values indicate faster performance. Fat-Free, with 0.0188 seconds, is clearly the faster framework by executing the task in a fraction of the time compared to the others. Yii is noticeably fast, while CodeIgniter, Symfony, and Laminas form a closely grouped second tier. Laravel is the slowest, taking over 24 times longer than Fat-Free to complete the same task. In terms of memory efficiency analysis, Fat-Free consumes the least amount of memory, reinforcing its minimalist design. At the opposite, Laravel uses by far the most memory, about 3.8 MB, consuming nearly 34 times more memory than Fat-Free. Should be noted how Yii and CodeIgniter consume a similar amount of memory, despite a two times difference in terms of defined components. Laminas and Symfony show similar value. The Accidental Complexity Score (ACS) In the context of this study, the essential complexity of the minimal test case is extremely low: simply loading the PHP environment and outputting the string "Hello World." Therefore, nearly all measured complexity and overhead during the framework's process execution represents accidental complexity. To provide a single, quantifiable measure of the framework's core architectural burden, the Accidental Complexity Score has been based exclusively on the volume of primary structural components loaded and instantiated in memory by the PHP interpreter. This metric is formalized as the sum of all object-oriented definitions and user-defined functions: ACS = classes + interfaces + traits + functions The chosen components are considered the most direct indicators of architectural complexity because they represent the object-oriented and procedural elements introduced by the framework. Two previously considered metrics measured by cesp_log, num_included_files and num_defined_constants, have been excluded from the final ACS. The number of included files primarily represents the I/O cost during a "cold start." Constants represent immutable values that consume minimal memory, and they arguably do not contribute to the complexity of the system's Page 7 of 11
Audax Development Research Notes - 1 / 2025-11-23 architecture in the same way that OOP elements and functions do. Based on the refined formula, the Accidental Complexity Score for each framework has been calculated in Table 3. Framework Declared Classes Declared Interfaces Declared Traits Defined Functions ACS Laravel 226 70 79 167 542 Symfony 135 59 11 68 273 Laminas 145 48 5 3 201 CodeIgniter 4 84 14 6 70 174 Yii Framework 40 5 0 10 55 Fat-Free Framework 11 0 0 2 13 Table 3: Accidental Complexity Score (ACS) for each framework Accidental Complexity Score and Architectural Complexity To interpret the Accidental Complexity Score as a meaningful measure of overhead, we must first define the concept of an Optimal Algorithm Implementation (OAI) for the given problem. This concept is not new and has its roots in a work on information and computation theory in the early 60s known as Kolmogorov Complexity, which formally defined the complexity of an object as the length of the shortest computer program required to describe it (Kolmogorov, 1998). While the exact Kolmogorov complexity is incomputable, the concept provides a theoretical foundation: any additional structure beyond the minimal implementation can be viewed as architectural complexity. Therefore, it is possible to define the Architectural Complexity (AC) of a framework as the excess structural complexity beyond the optimal implementation: AC = ACS - OAI In our test the Optimal Algorithmic Implementation (OAI) for a trivial “Hello World” as having zero structural elements, so the Architectural Complexity is equivalent to the Accidental Complexity Score. Summary of Findings The comparative data reveals a relationship between a framework's accidental complexity and its runtime execution performance and resource consumption (Figure 2 and Figure 3) . Page 8 of 11
Audax Development Research Notes - 1 / 2025-11-23 The empirical results also align strongly with Brooks’ scepticism regarding the ability of high-level abstractions, particularly Object-Oriented Programming, to meaningfully reduce accidental Page 9 of 11 Figure 2: Accidental Complexity Score (ACS) and Execution Time Laravel Symfony Laminas CodeIgniter 4 Yii Fat-Free 0 100 200 300 400 500 600 542 273 201 174 55 13 ACS Execution Time Delta (ms) Figure 3: Accidental Complexity Score (ACS) and Memory Usage Laravel Symfony Laminas CodeIgniter 4 Yii Fat-Free 0 100 200 300 400 500 600 542 273 201 174 55 13 ACS Memory Usage Delta (kB/10)