I think a review of general code quality, clean coding practices, and automated testing is worth including as well.
I've attached a system prompt snippet to teach an LLM composite design, which is the best way I know for determining code quality.
A prompt would then go something like:
"Evaluate this codebase according to the principles of composite design. Summarize the module strength and coupling of the various parts of the codebase. Inspect how well the included unit, integration, and end-to-end tests validate the intended function of the program."
---
## Software Design Principles
The following is a methodology for evaluating software code according to Composite Design (Glenford Myers, 1975) in the form of Functional Decomposition. Understand this criteria when writing and analyzing code, aiming for the highest module strength and loosest coupling possible.
### Definitions
- **Module**: A group of program statements that have a defined beginning and end, collectively referenced by a module name, and can be called from other modules. Corresponds to the "function" in most modern languages.
### Problem Structure
Composite analysis begins with the problem structure. A piece of software is typically comprised of several problem structures. The problem structure is a rough sketch of the problem the software is attempting to solve. It is presented in functional, rather than procedural, terms. It is typically a linear chain of three to ten processes, representing the data flow within the problem (not necessarily the execution flow, or the order the processes run in).
For example, the problem structure of an airline reservation system might be
- Input transaction
- Read flight files
- Update flight files
- Update accounts-receivable file
- Write ticket
- Send load & trim data
- Return flight information
### External Data Streams
After the problem structure is defined, the next step is to identify the streams of data that are both external and conceptual. External streams of data are those that begin and/or end outside of the system. Conceptual streams of data are those containing related data.
### Points of Highest Abstraction
From the identified external streams of data, the next step is to determine the points of highest abstraction for those streams. Data is transformed as it moves through the problem. From the starting input, there comes a point where input stream seems to disappear, as its data has transformed into a form that no longer resembles its origins. That point is called the Most Abstract Input Data. When the problem is viewed in reverse, from the ending output, a similar point can be found, the Most Abstract Output Data. The section of the problem in between the Most Abstract Input and the Most Abstract Output is the Central Transform. Sometimes, there is no Central Transform, and the point of Most Abstract Input and Most Abstract Output are the same.
Visually, the parts of the problem structure become as follows:
Major Input Stream -> Most Abstract Input Data -> Central Transform -> Most Abstract Output Data -> Major Output Stream
### Functional Decomposition
Following from the determination of the points of highest abstraction, the next step is decomposition. The problem structure is broken into three parts: the major input stream, the central transform, and the major output stream. Take each part and describe it as a function, with its inputs and outputs. The input and output streams usually have no input nor output, respectively. From these three submodules, the process of composite analysis can repeat, treating each as a subproblem, which will have its own problem structure and points of highest abstraction. The recursion continues until the subproblems cannot be broken down further.
#### Formatting Decompositions
If a program contains modules A, B, and C, where A calls B and C, and B calls C, the functional decompositions would be notated as follows. Each module has a name and is referred to with a capital letter starting with A, B, C... then AA, AB, AC... and so on. The first column shows whether the called module has other dependencies, with "β" indicating a root module and "β" indicating a mid-level module, which must have a section listing the modules it calls, until only root modules remain. Call numbers are sequential and unique, even if the same module is called in several other modules, and do not indicate the order in which modules are called.
---
**A. Name of Module A**
| | Call # | Module | Module Name | Inputs | Outputs |
| --- | ------ | ------ | ---------------- | ------ | ------- |
| β | 1 | B | Name of Module B | A | X |
| β | 2 | C | Name of Module C | B | Y |
**B. Name of Module B**
| | Call # | Module | Module Name | Inputs | Outputs |
| --- | ------ | ------ | ---------------- | ------ | ------- |
| β | 3 | C | Name of Module C | C | Z |
---
### Evaluating Software
#### Module Strength
In composite design, there are several levels of module strength. In the generated code, make each module as strong as possible.
- **Functional**: The strongest module. All of the elements are related to the performance of a single function.
- **Informational**: Performs multiple functions where the functions all deal with a single data structure (like a mapping). Represents the packing together of two or more functional-strength modules.
- **Communicational**: A module with procedural strength, but all the elements either reference the same set of data or pass data among themselves.
- **Procedural**: Perform more than one function, where they are related to the procedure of the problem.
- **Classical**: A module with logical strength, but the elements are also related in time, e.g. sequentially.
- **Logical**: A module of coincidental strength, but the elements are related logically.
- **Coincidental**: A module in which there is no meaningful relationship among its elements.
#### Module Coupling
In composite design, there are several degrees of module coupling. In the generated code, make each module as loosely coupled as possible.
- **Data**: A relationship that is not content, common, external, control, or stamp coupled. All input and output are passed as arguments or return variables. All arguments are data elements, not control elements or data structures.
- **Stamp**: A relationship where both modules reference the same data structure, and the data structure is not globally known.
- **Control**: A relationship where one module passes control elements to another. Such an argument directly influences the execution of the called module, such as function codes, flags, and switches.
- **External**: A relationship where both modules refer to the same global variable, but it is an individual data item, not a data structure.
- **Common**: A relationship where both modules refer to the same global data structure (such as a COMMON environment in FORTRAN).
- **Content**: A relationship where one module makes direct reference to the internal contents of another module, like program statements or private variables.