Software Engineering: Reasons and Limits
This non-normative rationale explains the Software Engineering philosophy. It neither establishes adoption nor adds obligations to that text. Its starting point is Organon Core 0.1.2; its additional value position concerns software maintained by people and agents across a stated lifecycle. Within that scope, the user favors continued evolution toward credible changes, while accepting concessions when its cost threatens concrete objectives. Credibility concerns identifiable reasons to expect a direction of change, rather than every imaginable extension.
The argument below distinguishes that value choice from Core's constraints and from engineering literature. Source summaries report limited contributions. Interpretations connecting them to this philosophy are the author's arguments, not conclusions attributed to those sources. Constructed counterexamples are identified accordingly.
What Core Contributes, and What It Leaves Open
Self-Transcendence challenges the finality of an architecture, toolchain, or design vocabulary. In software, that gives a reason to retain the possibility of learning a better decomposition. An additional premise is needed to prefer investing in that possibility over completing a bounded product: future constructive capacity must matter within the product's lifecycle. Core itself allows justified stability. A constructed counterexample is a disposable converter whose validated output completes its purpose; replacing its straightforward implementation with an extension framework need not expand anything the maintainers value.
Internal Consistency helps distinguish incompatible commitments from ordinary trade-offs. A system cannot coherently promise a particular freshness guarantee while accepting stale results under precisely that guarantee. Applying this constraint needs explicit meanings of freshness, acknowledgment, and relevant execution conditions. It does not choose those meanings or establish their adequacy. As a constructed counterexample, two components may consistently implement the same erroneous unit conversion. Consistency exposes conflicts within a position; it does not supply the missing domain truth.
Reflexivity brings architecture rules and agent instructions into the same field of criticism as application code. A rule intended to reduce coordination can itself become a source of coordination overhead. The additional engineering premise is that such overhead matters to the maintainers' objectives. A constructed counterexample is a review rule that demands another review of every review without an identifiable remaining question. Reflexivity makes that rule assessable; it neither proves the review effective nor entails this endless procedure or any particular independent-review arrangement.
Grounds: articulation and assessment separates a convincing explanation from sufficient support. A claim about throughput calls for evidence about the relevant workload; a claim that an interface isolates change calls for an argument about dependencies and change scenarios; a preference for future flexibility calls for reasons and acknowledged consequences. The engineering application supplies those workloads and scenarios. A constructed counterexample is a perfectly documented abstraction whose supposed future customers never materialize. Its rationale was articulable, but its forecast may have been poor.
Grounds: scope and measurement prevents a local success from settling a broader architectural comparison. Equal outputs on unchanged inputs leave concurrency, updates, failure handling, and maintenance effort unexamined. Additional premises determine which of those differences matter here. The limit also applies to negative judgments: an isolated incident may justify investigating a failure without establishing a population-wide defect. A constructed counterexample is rejecting a useful failure report solely because it has not yet been reproduced. Neither numerical scores nor repeatability alone resolves relevance.
Capability claims need a declared object of assessment. An agent may reliably perform a transformation in a constrained repository without understanding every reason for the architecture. Core permits external evidence for the former claim; it does not universally require introspective explanation. Requiring an understandable handover is an additional application choice, justified here by continuing maintenance. A constructed counterexample is an opaque but reliable generator supporting a maintainable product through stable interfaces and inspectable outputs. Its opacity alone does not establish incapacity, although a stronger understanding claim remains unsupported.
Openness to Alternative Implementations blocks priority based solely on a pattern's reputation. The contribution is substantive: neither SOLID, functional programming, nor metaprogramming establishes its own suitability. Engineering adds the objectives and constraints that make comparison possible. A constructed counterexample to indiscriminate novelty is replacing a familiar, supported platform with an unfamiliar alternative that offers identical relevant behavior but makes handover harder. Familiarity can supply a practical reason; calling an implementation conventional does not refute it.
These commitments constrain one another without prescribing a hierarchy of software values. Their conjunction does not entail that evolution takes priority over present abstraction simplicity once relevant necessary requirements are met. This philosophy adds that defeasible preference because its intended maintainers expect continued responsibility for changing software. Conversely, compatibility with Core would not by itself justify adopting the preference. Its merit depends on the lifecycle, the credibility of expected changes, and what maintaining those possibilities costs.
Engineering Arguments and Their Boundaries
Parnas compares decompositions that distribute processing steps with decompositions that hide design decisions. His argument supports examining which knowledge crosses a module boundary. It does not equate good structure with a particular runtime arrangement: he explicitly discusses the call overhead of a mechanical implementation and alternatives that preserve the decomposition while assembling efficient code. This is an original condition, not a later exception invented to protect modularity. Parnas, “On the Criteria To Be Used in Decomposing Systems into Modules,” pp. 1056–1058.
For SRP, Martin's later explanation locates responsibility in the business function requesting change and connects cohesion with common reasons for change. This supports investigating why code changes together, rather than counting methods. Constructed counterexample: separating validation, calculation, and explanation solely because they are different verbs can scatter a single business-rule revision across independently maintained components. That example is this rationale's objection to a mechanical interpretation, not Martin's empirical finding. Martin, “The Single Responsibility Principle,” 2014.
For OCP, Martin explicitly limits closure to selected changes. His original drawing example accommodates new shapes without making the drawing loop immune to a new ordering requirement. The useful question is therefore which change an extension mechanism protects and which it leaves exposed. A promise of universal extensibility overstates this source; modifying an existing boundary can be a reasoned response to a different change direction. Martin, “The Open-Closed Principle,” p. 6.
For LSP, Liskov and Wing make subtype judgments depend on specifications and preservation of observable properties, including invariants and history properties. Their window example shows why allowing additional movements can violate a property relied on by existing clients. Matching signatures or leaving inherited methods untouched is insufficient. Applying similar reasoning to protocols or generic parameters is a proposed transfer of the idea; their paper does not establish every such application. Liskov and Wing, “A Behavioral Notion of Subtyping,” §§5.2–5.3.
For ISP, Martin examines clients forced to depend on interfaces they do not use. Separating those dependencies need not split the stateful object: the timed-door example implements distinct client interfaces over shared data. The original case therefore challenges the inference that interface separation always calls for object separation. Whether a proposed division reduces consequential coupling remains a question about the actual clients. Martin, “The Interface Segregation Principle,” pp. 4–6.
For DIP, the concern is the relationship between higher-level policy, abstractions, and implementation details. Martin includes C standard I/O as an example, so the mechanism is not confined to inheritance frameworks. Constructed counterexample: an interface that reproduces a vendor's schema and exceptions can retain dependence on those details despite dependency injection. The architectural claim then needs more than an interface declaration. Martin, “The Dependency Inversion Principle,” pp. 5–6.
Martin's broader account supplies important qualifications across these principles: static templates can realize OCP; ISP does not require a distinct interface for every client class; stable concrete dependencies can be reasonable. His warning that a character-encoding change can undermine the last judgment preserves its conditions. These qualifications support contextual use rather than a compulsory inventory of abstract classes. Martin, “Design Principles and Design Patterns,” pp. 7, 14–16.
Refactoring has a behavioral boundary. Fowler defines it in terms of internal restructuring that makes software easier to understand and cheaper to modify while preserving observable behavior. That supports distinguishing structural preparation from a feature change; the definition alone does not determine a project's observations. Constructed counterexample: a rewrite preserves returned values but changes externally relied-on failure behavior. Calling it refactoring cannot settle whether that behavior was within the preservation claim. Fowler, “Definition of Refactoring”.
Functional composition offers another route to decomposition. Hughes shows how higher-order functions and lazy evaluation separate reusable computation patterns and separate generation from selection. His argument does not make the absence of assignment a sufficient quality measure. An original qualification concerns side effects whose timing becomes dependent on distant consumption. The relevant benefit is a useful compositional boundary, not adherence to a vocabulary or a blanket ban on state. Hughes, “Why Functional Programming Matters,” §§1–4.
Generic programming likewise needs common semantics, not merely matching syntax. Dehnert and Stepanov argue from operational laws and complexity expectations. Their iterator discussion explains why providing random-access-shaped operations over a different complexity model can mislead algorithm selection. This supports examining the assumptions hidden in reusable algorithms; it does not justify requiring every identity-bearing or resource-owning object to model a regular value. Dehnert and Stepanov, “Fundamentals of Generic Programming,” pp. 3–5, 10–11.
Template metaprogramming and partial evaluation explain how information available earlier can specialize later computation. Veldhuizen relates template instantiation to offline partial evaluation while distinguishing parameterized reuse, compile-time computation, and generation. His recursive-instantiation example also exposes termination limits. The connection can explain a benefit; it cannot establish that specialization is free or that every parameter deserves static binding. Veldhuizen, “C++ Templates as Partial Evaluation”.
Expression templates offer a concrete case: Veldhuizen demonstrates avoiding temporary vectors and combining evaluation, under particular benchmark conditions. Those results do not establish a universal advantage for expression templates. Veldhuizen, “Expression Templates”. Iglberger and colleagues subsequently show limitations in matrix, sparse, and compound operations, motivating different evaluation strategies and optimized kernels. Their experiments are a counterexample to unconditional performance claims, not evidence about every current library version. Iglberger et al., “Expression Templates Revisited,” §§5–8.
This rationale draws a further engineering inference: moving work from execution to compilation changes where cost and restrictions appear. A useful comparison might include build latency, generated-code size, diagnostics, deployment combinations, runtime dispatch, and the expertise required to modify the mechanism. Constructed counterexample: a finite configuration known at release may suit specialization, while customer-loaded behavior may suit runtime selection. Neither arrangement wins solely because it moves decisions earlier or later.
Composition and Inheritance
The LSP argument above concerns behavioral subtyping. It does not by itself choose between inheriting implementation and composing collaborators for reuse. The following comparison is this rationale's engineering argument. Inheritance can provide a useful shared implementation when the subtype contract and supported variation points are stable. Composition can let maintainers replace a collaborator without claiming that the containing object is a subtype of it. Neither mechanism alone establishes independent change: a delegating wrapper that exposes its collaborator's exceptions and lifetime requirements may preserve the same consequential dependencies.
A constructed example is a service with an evolving retry policy. A policy collaborator can permit replacement without editing an unrelated storage hierarchy. But if retries, transactions and acknowledgment jointly determine observable behavior, separating their classes does not make their changes independent. An inherited framework with explicit, stable hooks may remain easier for its actual maintainers to understand and verify than a network of forwarding objects. Compare the contracts, permitted overrides, ownership and propagation of the credible change. Revisit the choice when those conditions change; do not prefer composition or inheritance solely by slogan.
A Reasoned Design Judgment
The preceding arguments suggest a contextual line of inquiry, not a mandatory scorecard. A design proposal can begin with a concrete change and explain what would have to be understood, modified, verified, and handed over under the present structure. Comparing a proposed boundary with retaining the current arrangement can then reveal whether it localizes a real decision or simply relocates complexity. One credible change may justify a boundary; numerous hypothetical changes may justify none.
The important inference concerns total consequences for the relevant maintainers. Fewer edited files can conceal a harder central abstraction. Additional modules can make distinct responsibilities independently intelligible. A specialized implementation can simplify its users' work while concentrating difficult machinery in a place with adequate expertise. These possibilities defeat fixed preferences about paradigm, layer count, or abstraction density. They do not make alternatives indistinguishable: the stated task and constraints can still favor one decisively.
Verification could combine behavior-preserving comparisons, exercises of the relevant change, examination of dependencies, and a maintainer's explanation of how the design works. These are suggested evidential approaches whose burden depends on the claim. Passing a narrow test supports its narrow result; a clear argument about future change remains conditional on its premises. Uncertainty can favor postponement, a small seam, or direct implementation rather than a general framework.
Withdrawal, Migration, and Continuing Responsibility
Evolution can include removing an abstraction. Under this proposal, its continued presence has no entitlement based on creation cost or architectural prestige. A constructed example is a policy framework whose supposedly independent variations have converged into one stable rule. Inlining that rule may improve comprehensibility and release maintainers from coordinating obsolete extension points. The relevant comparison includes what would become harder to change as well as what becomes simpler now.
Migration introduces a different question from the desirability of the destination. A simpler representation can be a poor immediate choice if reaching it loses required data, breaks dependent consumers, or exceeds an operational budget. Reversal may require restoring data and coordination state, not merely reverting code. As an application suggestion, staged replacement can expose these consequences while the old behavior remains available; retaining both versions also has costs and cannot be presumed preferable indefinitely.
Handover provides a concrete challenge to claims of maintainability. Future people and agents inherit interfaces, assumptions, failure modes, and tools, rather than the author's private understanding. This rationale therefore treats reconstructible reasons and usable modification paths as valuable. It does not infer that every contributor must explain every internal mechanism. Maintainers can reasonably rely on constrained generators, specialist ownership, and stable components when those arrangements remain credible over the chosen lifecycle.
There are strong reasons to reject or override evolution priority in particular circumstances. A short-lived artifact may have no valuable future extension. A verified boundary may be expensive to reopen. Runtime constraints, deployment size, interoperability, or scarce maintenance expertise may make a flexible design threaten the product's purpose. Predictions can also privilege imagined future users over people bearing present costs. These are substantive objections to weigh, not failures to appreciate architecture.
The principal trade-off is how much present complexity to accept for credible future capability. The preference gives that capability weight without proving its value in every case. Remaining gaps include how a project establishes its lifecycle, whose maintenance burden counts, and what evidence makes a forecast credible enough to guide an expensive choice. This rationale offers reasons for examining those questions; it neither supplies universal thresholds nor certifies adoption.
Clause Map
This map makes the proposal's reasons and revision conditions inspectable. It explains the five domain units without adding obligations to them.
| Unit | Problem, premises and support | Alternatives, counterexample and revision grounds |
|---|---|---|
| 4.1 Purpose, subjects and lifecycle | An author's ability to edit can conceal a successor's inability to maintain. Continuing responsibility makes tools, knowledge and handover relevant to the capability claimed. | An individual prototype or disposable artifact may need only bounded completion. Changed actual maintenance responsibility or retirement expectations can warrant reconsidering the scope; a label alone does not establish that change. |
| 4.2 Priority for credible evolution | Optimizing present abstraction simplicity can impose avoidable later costs when a change direction has identifiable grounds. The user values that future capability and accepts some present complexity for it. | Present simplicity, postponement and a small seam remain alternatives. A forecast that never materializes, or flexibility that threatens a concrete objective, can defeat a particular investment. This value preference remains criticizable even when the forecast is accurate. |
| 4.3 Meaning of evolution | Extension within one scheme can coexist with an expensive exit. Continued maintenance includes withdrawal, deletion and migration when those changes matter. | A deliberately fixed scope can be adequate for a bounded product. Cheap extension does not prove cheap replacement; different relevant obligations or change directions can warrant reconsidering the judgment. No design must make all changes cheap. |
| 4.4 Structural judgments | Interface shape and pattern names omit behavior, dependencies and verification work. Whole engineering consequences connect a design choice to the capability claimed. | Direct code, abstraction, static specialization and runtime selection can each be supported under different constraints. A signature-compatible replacement that breaks required ordering is a counterexample to shape-based assurance. Changed contracts require a corresponding new judgment. |
| 4.5 Revision and stability | Failed forecasts and sunk investment can preserve mechanisms after their rationale has weakened. Core's non-finality and Grounds support revisiting that rationale while permitting justified retention. | Retaining a design can be justified when alternatives lack a supported contribution or defeat the objective. A costly rewrite with no relevant benefit rebuts compulsory change. Materially changed grounds can warrant another decision without rewriting what supported the earlier one. |