Software Engineering: Process, Requirements, Design and Quality

Software Engineering: Process, Requirements, Design and Quality

Comprehensive software engineering notes on lifecycle, project management, quality, maintenance, requirements, design, architecture, and reproducible AI/ML configuration.

Process models, project management, quality, testing, maintenance, requirements, and design are treated as connected engineering concerns rather than isolated definitions. Classical methods remain important in their historical context, while DevOps, continuous delivery, and current quality and life-cycle standards represent later practice and are identified accordingly.

Unit 1: Introduction to Software Engineering

Software and software engineering

A program is executable logic; a software product also includes data, configuration, interfaces, documentation, deployment artifacts and operational assumptions. Software engineering applies systematic engineering methods to the specification, design, construction, verification, operation and evolution of such systems.

The purpose is not bureaucracy. It is to control complexity, change and risk when software is larger or longer-lived than one developer can safely hold in working memory.

Program, software and hardware

Hardware fails through physical mechanisms and aging. Software does not wear out in the same physical sense, but it degrades operationally when changes accumulate, dependencies evolve and assumptions no longer match the environment.

Software maintenance is therefore primarily change management, defect correction, adaptation and controlled evolution.

Software engineer and production environment

Writing correct code is only part of engineering work. Production software also requires:

  • version and configuration control,
  • build/release repeatability,
  • testing,
  • deployment and rollback planning,
  • observability,
  • security controls,
  • capacity and failure analysis,
  • documentation of interfaces and assumptions.

Classification of software systems

Software can be embedded, real-time, interactive, batch, scientific, information-system, safety/security critical, mobile, distributed or infrastructure software. These categories overlap. The engineering method must follow the dominant risks of the system rather than one universal process template.

Software crisis and continuing complexity

The historical "software crisis" described projects that exceeded cost/schedule, delivered unreliable systems or became impossible to maintain as complexity grew.

Modern tools improved languages, automation and infrastructure, but the underlying problem remains: requirements change, interfaces multiply, systems become distributed and human coordination does not scale automatically with code size.

Main sources of software problems

Common causes include:

  • incomplete or changing requirements,
  • weak architecture,
  • uncontrolled dependencies,
  • underestimated integration effort,
  • poor testability,
  • unowned technical debt,
  • unrealistic schedules,
  • weak configuration/release discipline,
  • insufficient feedback from production.

Error propagation through the life cycle

A misunderstood requirement can become a design decision, then code, then test assumptions and eventually operational failure. Detecting a defect earlier usually reduces the cost of correction because fewer dependent artifacts must change.

This does not mean every requirement can be frozen early; it means feedback cycles should expose errors before they propagate too far.

Software development life cycle

A generic lifecycle contains:

need / concept
    ↓
requirements
    ↓
design
    ↓
implementation
    ↓
verification and validation
    ↓
deployment / transition
    ↓
operation and maintenance
    ↓
retirement

The phases can overlap and iterate. ISO/IEC/IEEE 12207:2026 provides a current common framework for software life-cycle processes, while earlier lifecycle formulations remain useful in their historical context.

Development plan

A project plan defines scope, deliverables, responsibilities, milestones, resources, risks, quality activities and change/control mechanisms. A plan should be updated from evidence; it is not useful if maintained only as a document detached from actual project state.

Requirements analysis

Requirements describe externally meaningful behavior, constraints and quality properties. They should be traceable to stakeholders and verifiable enough that completion can be evaluated.

Design

Design converts requirements into architecture, components, data structures, interfaces and responsibilities. Good design controls coupling and makes expected change local rather than spreading through the system.

Testing

Testing executes or evaluates the system to find failures and gather evidence about conformance. Tests cannot prove the absence of every defect in a non-trivial program; they sample behaviors according to a designed strategy.

Maintenance and evolution

Maintenance includes corrective, adaptive, perfective and preventive change. A system with poor modularity or test coverage may spend most of its lifecycle cost after initial delivery.

Process maturity

Process maturity means the organization can perform and improve engineering activities predictably. It should not be confused with the number of forms or approvals. A repeatable automated process can be more mature than a document-heavy process that is not followed.

Process models

Different process models organize uncertainty and feedback differently.

Waterfall

A strict waterfall treats stages as mostly sequential. It is easy to explain and can fit domains with stable requirements and formal gates, but it performs poorly when important uncertainty is discovered late.

V-model

The V-model pairs specification/design levels with corresponding verification levels. It emphasizes planning tests against requirements and architecture rather than waiting until implementation is complete.

Prototyping

A prototype reduces uncertainty about interfaces, usability or technical feasibility. A throwaway prototype should not silently become production architecture without re-evaluating quality requirements.

Fourth-generation/code-generation approaches

Historical fourth-generation tools aimed to generate large parts of applications from higher-level models or declarative descriptions. Modern low-code tools, ORM generators, schema compilers and model-based toolchains continue the same idea in different forms.

Evolutionary and research-oriented development

When requirements are uncertain, iterative experiments can reduce technical risk. The important control is to distinguish exploratory code from components that have been hardened for production constraints.

RAD

Rapid Application Development emphasizes short cycles, prototyping and reuse. Speed is valuable only if quality and long-term maintainability are not deferred indefinitely.

Incremental development

An incremental model delivers the system in usable slices. Each increment should preserve architecture and integration discipline; otherwise increments simply accumulate incompatible local solutions.

Spiral model

The spiral model is risk-driven. Each cycle identifies objectives, analyzes risks, develops/validates a solution and plans the next cycle. It is especially useful conceptually when technical risk should determine what to prototype or prove first.

Agile development

Agile approaches emphasize short feedback cycles, working software, collaboration and adaptation. They do not remove architecture, testing or documentation; they move decision-making toward continuous evidence and incremental delivery.

DevOps and continuous delivery

DevOps extends responsibility across development and operations through automation, shared feedback and operational ownership. Continuous integration and delivery reduce batch size between code change and deployable artifact.

A high-frequency pipeline is useful only when tests, deployment controls, observability and rollback are reliable enough to keep change risk bounded.

CASE tools

Computer-Aided Software Engineering historically grouped modeling, code generation, documentation and project-support tools. Modern IDEs, CI systems, static analyzers, issue trackers and model/code generators can be understood as a more distributed engineering toolchain.

Estimation uncertainty and decision ranges

A software estimate is not a measurement of the future. Function points, LOC-based models, COCOMO-style equations, expert judgment, and historical velocity use different proxies and inherit uncertainty from scope, team, technology, and data quality.

A useful estimate should therefore state assumptions and a range rather than one precise number. Re-estimation after requirements or architecture change is normal engineering behavior, not evidence that the original estimate was necessarily careless.

Risk registers should connect each material risk to an owner, trigger, mitigation, and contingency. A long unprioritized risk list is not a control mechanism.

Unit 2: Software Project Management

Purpose

Project management coordinates scope, time, cost, people, quality and risk. Software projects are difficult because progress is less physically visible than in many engineering disciplines and because late integration can expose hidden dependencies.

Project resources

Resources include:

  • people and expertise,
  • computing environments,
  • licenses/tools,
  • test hardware/data,
  • time,
  • external suppliers and dependencies.

Adding people to a late project can increase coordination and onboarding cost; staffing is not a linear throughput control.

Team organization

Teams need clear ownership and communication paths. Centralized, decentralized and mixed structures have different strengths. Architecture ownership should not mean one person becomes a throughput bottleneck; decisions and interfaces must be documented enough for collective execution.

Software measurement

Direct measures include values such as elapsed time, defect count, build duration and resource consumption. Indirect measures try to represent qualities such as maintainability or complexity.

Metrics can distort behavior when treated as targets without context. Lines of code, commit count or ticket closure count are particularly poor measures of individual productivity.

Function points

Function Point Analysis estimates functional size from user-visible inputs, outputs, inquiries, files and interfaces. It was designed to be less language-dependent than LOC.

Its value depends on consistent counting and suitable project type; it is not a universal predictor of engineering effort.

LOC versus FP

LOC is objective after code exists but varies with language, generation and style. FP estimates functionality earlier but requires interpretation. Both are proxies, not direct measures of value or complexity.

Cost estimation

Estimation methods include:

  • expert judgment,
  • analogy,
  • Delphi/wideband consensus,
  • parametric models,
  • decomposition and bottom-up estimation.

An estimate should include uncertainty rather than presenting one precise number as certainty.

COCOMO

Basic COCOMO effort growth versus KLOC for organic semidetached and embedded project modes
COCOMO effort curves

COCOMO is a parametric effort-estimation family based on software size and cost drivers. Its historical forms are useful for understanding nonlinear effort growth. Any real use requires calibration to contemporary technology and organization rather than blindly applying old coefficients.

Putnam-Norden-Rayleigh model

The Putnam approach models staffing/effort over time with a Rayleigh-like curve and relates size, effort and schedule. It demonstrates an important principle: aggressively compressing schedule can increase required effort disproportionately.

Risk management

A risk is an uncertain event/condition with potential impact. A useful risk record includes probability, impact, trigger/indicator, owner and response.

Risk categories can include:

  • product size/scope,
  • business/customer,
  • process,
  • technology,
  • development environment,
  • staffing,
  • external dependency.

Responses include avoid, reduce/mitigate, transfer/share or accept with contingency.

Project schedule

Project activity network with durations and the longest critical path highlighted
PERT critical path

A schedule should reflect dependency structure, not simply assign arbitrary dates to every task.

Gantt charts show task timing. PERT/CPM model precedence and critical paths. The critical path contains activities whose delay can delay completion when there is no available float.

Unit 3: Software Quality Management and Testing

Software quality

Quality means fitness relative to explicit and implicit requirements. It includes functional correctness but also performance, reliability, security, maintainability and other properties.

ISO/IEC 25010:2023 defines the current product-quality model with nine characteristics, superseding earlier quality-model formulations.

Quality metrics

Metrics should correspond to engineering decisions. Examples include defect escape rate, failure rate, response-time percentiles, resource utilization and code-complexity indicators.

Halstead metrics

Halstead metrics derive values from counts of distinct/total operators and operands. They are historically important quantitative code metrics but should not be treated as direct measures of human cognitive difficulty or quality.

McCabe cyclomatic complexity

Control-flow graph illustrating independent paths and McCabe cyclomatic complexity
McCabe cyclomatic complexity

Cyclomatic complexity measures independent control-flow paths. For a connected control-flow graph:

V(G) = E - N + 2

and for structured code it is also related to the number of decision points plus one. High complexity can indicate testing/maintenance difficulty, but a low number does not guarantee good design.

Quality assurance and quality control

Quality assurance focuses on processes that prevent defects and establish confidence. Quality control evaluates work products and finds nonconformance. Reviews, automated checks and testing can contribute to both depending on context.

Technical reviews

Code/design reviews find defects, spread knowledge and challenge assumptions before deployment. Effective review has a defined scope and should focus on correctness, maintainability and risk rather than personal style preferences.

Test levels

Common levels are:

  • unit/component,
  • integration,
  • system,
  • acceptance.

The boundaries depend on architecture, but each level should have a clear purpose and test oracle.

Unit testing

A unit test exercises a small component under controlled dependencies. Test doubles/drivers/stubs may isolate collaborators, but excessive mocking can test the mock arrangement rather than real behavior.

Integration testing

Integration tests verify component interactions, protocols, database mappings and lifecycle assumptions.

Historical top-down integration begins from high-level components with lower stubs; bottom-up integration begins from lower components with drivers. Contemporary systems often combine incremental vertical slices with automated contract/integration tests.

System and acceptance tests

System tests validate end-to-end system behavior against system requirements. Acceptance testing evaluates whether the delivered system is suitable for stakeholder/business use under agreed criteria.

Debugging

Testing reveals a failure; debugging finds and corrects its cause. Reproducing the failure deterministically, narrowing the state difference and validating the correction are separate steps.

White-box testing

White-box techniques use implementation structure. Basis-path testing relates tests to control-flow paths. Loop testing considers zero, one, boundary and repeated iterations according to permitted ranges.

Exhaustively covering every path is generally impossible when loops and conditions combine, so coverage is evidence rather than proof.

Black-box testing

Black-box techniques derive tests from externally specified behavior.

Equivalence partitioning groups inputs expected to behave similarly.

Boundary-value analysis targets edges where defects are common.

Cause-effect graphs and decision tables model combinations of conditions and resulting actions.

Test planning

A test plan defines scope, environments, data, responsibilities, entry/exit criteria and evidence. Test specifications should identify expected results and preconditions so a passing test means something reproducible.

Verification and validation

Verification asks whether the work product satisfies its specification; validation asks whether the resulting system satisfies its intended use. Both activities should occur throughout the lifecycle.

Regression testing

Regression tests check that existing supported behavior still works after change. A regression suite should be fast and reliable enough to run frequently; flaky tests reduce trust and therefore reduce actual protection.

Continuous testing

Modern CI pipelines execute tests, static analysis and packaging automatically on changes. Production confidence also requires integration, performance, security and deployment verification that may run at different cadences.

Unit 4: Maintenance and Configuration Management

Maintenance

Maintenance includes:

  • corrective changes,
  • adaptive changes,
  • perfective improvements,
  • preventive refactoring/hardening.

The classification is useful, but one change can belong to more than one category.

Installation and field support

Delivery includes installation, migration, configuration and operational support. A product that works only in the developer environment is not production-ready.

Maintainability

Maintainability improves when modules have clear responsibilities, interfaces are stable, tests cover behavior and operational diagnostics identify failures quickly.

Reducing maintenance cost

Useful practices include:

  • simple architecture,
  • controlled dependencies,
  • readable code,
  • automated tests,
  • reproducible builds,
  • documented data migrations,
  • compatibility policies,
  • observability.

Software configuration

A configuration identifies exact versions of source, dependencies, build inputs, deployment parameters and sometimes infrastructure/data schema required to reproduce a system state.

Configuration management

SCM controls:

  • identification,
  • versioning,
  • change control,
  • status accounting,
  • audit/release baselines.

Baseline

A baseline is an agreed configuration used as a reference for further change. In modern delivery, signed/versioned build artifacts and immutable releases often play this role.

Change control

Not every change needs a committee, but every production-impacting change needs an accountable path: why it exists, what it changes, how it was verified and how it can be reverted or mitigated.

Configuration audit and status reporting

An audit checks whether delivered artifacts correspond to the declared configuration. Status reporting records which change/version is in which environment and whether required controls have completed.

Version control

Distributed version-control systems make branching/merging inexpensive, but repository history alone is not release management. Builds must still map commits to reproducible artifacts and deployment state.

Modern configuration chain

A contemporary chain can be:

source commit
  ↓
locked dependencies
  ↓
reproducible build
  ↓
versioned artifact/container
  ↓
environment configuration
  ↓
deployment record

Secrets should not be embedded into source or ordinary configuration artifacts.

Reproducible configuration for AI and machine-learning systems

A software release that contains AI or machine learning cannot always be identified by a source-code commit alone. The same code can produce different behavior with different data, preprocessing rules, model weights, prompts, or decision thresholds.

A reproducible release context may therefore include:

source code
+ data / dataset version
+ preprocessing and feature rules
+ schema / label contract
+ model or checkpoint
+ training and evaluation configuration
+ prompt / template version
+ evaluation set
+ decision thresholds
+ deployment configuration
= reproducible execution context

Not every project requires every item. The durable rule is that artifacts capable of changing system behavior must be visible configuration items.

Data and model provenance

If it is unknown which data, preprocessing version, and source revision produced a model, reproducing a result or locating a regression becomes difficult. Dataset versions, model artifacts, and source commits should therefore be traceable to one another rather than encoded only in filenames.

data version
    ↓
training configuration
    ↓
model artifact
    ↓
evaluation result
    ↓
deployed release

This lineage answers more than “which model had the best score?” It records why a model was released and what evidence supports rollback or replacement.

Evaluation data is configuration too

Changing the evaluation set can change the measured result of an unchanged model. Comparisons should therefore record the evaluation-data version, filtering rules, and metric definition. Repeatedly tuning against the same test set can leak test information into development and weaken its role as independent evidence.

The testing perspective is developed further in Software Test Engineering.

Separating training and serving behavior

When production input preparation diverges from the training pipeline, training-serving skew can appear. Normalization, category mapping, tokenization, and feature-generation contracts should be traceable across both environments.

Versioning a model file alone does not solve this problem. The model's expected input and output contracts are also part of the release.

Gradual rollout and rollback

A new model or threshold does not always need to receive all production traffic immediately. Depending on risk, a release can begin with a limited cohort, shadow evaluation, or controlled comparison. Such rollout is useful only if the previous artifact and configuration remain recoverable.

new release
→ limited validation
→ measurement
→ expand or roll back

This does not separate model development from configuration management. It extends configuration management to data and model artifacts. Experiment design, model selection, and production observation are discussed from the model-development perspective in Artificial Neural Networks and Learning Models.

Unit 5: Software Requirements Analysis

Requirements

Requirements describe behavior or constraints expected from the system.

Functional requirements specify what the system does.

Non-functional requirements specify qualities/constraints such as latency, availability, security, capacity, auditability or portability.

A statement such as "the system shall be fast" is not sufficiently verifiable. A requirement should define measurable conditions where the property matters.

Requirements specification

A specification should be correct, consistent, sufficiently complete, unambiguous, traceable and verifiable for its intended use. These are goals; real projects manage uncertainty rather than pretending it does not exist.

Requirement versus design

"The system shall authenticate users" is a requirement. "Use protocol/library X" is usually a design constraint unless a stakeholder/regulation specifically requires it.

Separating need from implementation preserves design freedom.

Traceability

Traceability links connecting requirements to design elements verification tests and deployment artifacts
Requirements traceability

Traceability links:

stakeholder need
 -> requirement
 -> design element
 -> implementation
 -> verification evidence

This is valuable in regulated or critical systems and also when impact analysis is difficult.

Airborne software is a concrete example of this evidence chain. Preserving links among requirements, design decisions, source code, verification results, configuration, and problem reports is a core part of software assurance. The DO-178C and avionics context is discussed in Avionics Systems and Unmanned Aerial Vehicles.

System-level safety constraints: requirements are more than feature statements

In a critical system, a requirement should not only describe what the system shall do. It may also need to state what the system must not do under a particular context. Components can satisfy their local specifications while their interaction still produces an unacceptable system state. For safety-critical and cyber-physical systems, requirements can therefore be derived backward from system-level losses and hazards.

unacceptable outcome
↓
hazardous system state
↓
constraint that must not be violated
↓
design decision
↓
verifiable requirement
↓
test / operational evidence

This does not replace functional or non-functional requirements. It exposes an additional dimension: a function that is correct in isolation can still be unsafe when executed in the wrong state, at the wrong time, or on stale feedback.

A requirement such as send command, for example, is incomplete when command validity depends on operating mode, data age, acknowledgement, physical state, or another controller acting on the same process. Those conditions belong to the engineering contract as much as the command payload itself.

This view is consistent with Nancy G. Leveson's systems-theoretic treatment of safety through control and constraints. The objective here is not to reproduce a method mechanically, but to distinguish a feature inventory from a specification of safe system behavior.

Traceability consequently becomes stronger than a simple requirement -> test link:

risk / hazard
-> system constraint
-> requirement
-> architectural element
-> implementation
-> verification evidence

When a requirement changes, the originating constraint and the evidence that justifies the behavior should be reconsidered as well.

Studying the existing system

Legacy behavior is often an undocumented source of requirements. Existing workflows, database constraints, operational scripts and failure workarounds should be investigated before replacement.

Elicitation techniques

Techniques include interviews, workshops, observation, questionnaires, document analysis, prototypes and analysis of actual production behavior.

Stakeholders frequently describe desired solutions rather than underlying needs; elicitation should distinguish the two.

Data modeling

Conceptual data models identify entities, relationships, lifecycle and constraints before physical schema decisions.

UI prototyping

UI prototypes expose workflow and terminology uncertainty early. Visual fidelity should match the question being tested; a low-fidelity workflow prototype is often more informative early than a polished mockup.

Evaluating the analysis model

Check whether every requirement is represented, conflicts are visible, system boundaries are clear and exceptional/error cases are modeled.

Formal specification

Formal methods express requirements using mathematically defined notations. They are valuable when ambiguity or proof obligations justify the cost, particularly in protocols, safety properties and critical algorithms.

State-oriented notations

State machines describe lifecycle transitions. Transition tables make event/state combinations explicit and are effective for protocols and control systems.

Decision tables

Decision tables enumerate combinations of conditions and actions. They help expose missing and contradictory business-rule combinations.

Petri nets

Petri nets model concurrency, synchronization and resource flow through places, transitions and tokens. Reachability and liveness analysis can expose deadlock-like behavior in modeled systems.

Data-flow analysis

Data-flow diagrams model external entities, processes, data stores and flows. They are historically important structured-analysis tools and remain useful when the problem is primarily transformation of information.

Data dictionary

A data dictionary defines names, meanings, structure, units, valid ranges and ownership of shared data elements. Modern API/schema registries and typed contracts continue the same discipline.

Quality attributes as architecture constraints

Architecture should make nonfunctional requirements measurable. Latency, throughput, availability, recovery time, consistency, security, deployability, and modifiability can pull the design in different directions.

A quality-attribute scenario states a source, stimulus, environment, artifact, response, and response measure. This turns statements such as “the system should be fast” into testable constraints.

Architecture fitness functions can automate part of this contract: dependency rules, latency budgets, schema compatibility, forbidden calls, resource ceilings, or security invariants can be checked continuously.

The goal is not to score architecture with one number. It is to make important design assumptions observable before production failure reveals them.

Unit 6: Software Design

Purpose of design

Design decides how responsibilities, state and interfaces will satisfy requirements while controlling change and operational risk.

Architecture and data design

Architecture defines major components, deployment boundaries and interactions. Data design defines ownership, schema, consistency and lifecycle of information. Treating persistent data as an afterthought creates coupling that is difficult to remove later.

Modularity

A module should have a coherent responsibility and a small explicit interface. Modularity is valuable when it reduces the number of components affected by one change.

Cohesion and coupling

High cohesion means related behavior belongs together. Low coupling means modules depend on few stable details of one another. These are design objectives, not absolute numeric properties.

Abstraction and information hiding

Abstraction exposes a useful model; information hiding conceals decisions likely to change. A stable interface should not leak internal storage, protocol or timing details unless callers genuinely need them.

Structured programming

Sequence, selection and iteration provide disciplined control flow. Structured programming historically replaced arbitrary branch-heavy programs and remains a foundation even when modern languages add exceptions, async control and functional constructs.

Preliminary and detailed design

High-level design identifies components and interfaces. Detailed design defines algorithms, state and local contracts. The right amount of detail is what makes independent implementation and verification possible without freezing irrelevant choices.

Structural design and historical flow transformations

Structured-design methods transformed data-flow models into module hierarchies. Their specific notation is historical, but the idea of deriving component boundaries from information flow remains useful.

Design notations

Pseudocode, flowcharts, structure charts, UML diagrams and interface definitions should be used when they make a decision clearer. Producing diagrams with no decision content is not documentation quality.

User-interface design

UI design should account for user tasks, feedback, errors, accessibility, consistency and latency. A technically correct backend can still produce an unusable system if interaction state is ambiguous.

Unit 7: Object-Oriented Analysis and Design

Object-oriented approach

Objects combine state with operations; classes define families of objects. Encapsulation, inheritance and polymorphism are mechanisms, not goals by themselves.

Analysis

Object-oriented analysis identifies domain concepts, responsibilities and interactions without prematurely deciding every implementation class.

Historical Coad-Yourdon and Booch methods influenced later UML-based modeling.

UML

Useful diagrams include:

  • class diagrams for structural relationships,
  • sequence diagrams for time-ordered interaction,
  • state-machine diagrams for lifecycle behavior,
  • activity diagrams for workflow,
  • package/component diagrams for dependency/architecture views,
  • use cases for actor-goal interactions.

A UML diagram should answer a question; not every project needs every diagram type.

CRC cards

Class-Responsibility-Collaborator cards are a lightweight technique for discussing what a class owns and whom it must collaborate with. Their value is in design conversation, not the physical card format.

Relationships

Association, aggregation/composition, dependency and inheritance communicate different semantics. Composition expresses ownership/lifetime more strongly than a generic association.

Concurrency and subsystem design

Concurrent objects/components need explicit synchronization and ownership of mutable state. Hiding a lock inside every object does not automatically produce a deadlock-free system; lock ordering and cross-component operations matter.

Design patterns

Patterns name recurring design structures. They are not mandatory templates. A pattern is useful when the forces that motivated it are present; using patterns for their own sake adds indirection.

SOLID connection

Later object-oriented design discussions often use SOLID principles:

  • single responsibility,
  • open/closed,
  • Liskov substitution,
  • interface segregation,
  • dependency inversion.

They are heuristics for managing change and substitution. They do not override simpler design when the abstraction has no real variability.

Composition over inheritance

Composition often reduces coupling to a base-class implementation and allows behavior to be assembled explicitly. Inheritance remains appropriate for stable substitutable type relationships.

Limits of object orientation

Not every problem is best decomposed into stateful objects. Data-oriented, functional, relational, actor/message and pipeline designs can be better depending on workload and invariants.

Unit 8: Implementation and Coding

Implementation converts design into executable artifacts while preserving interface and quality constraints.

Development environments and languages

Tooling should provide reproducible compiler/runtime versions, diagnostics, tests and build automation. Language selection depends on ecosystem, runtime, performance, safety, platform and maintainability constraints.

Database systems and libraries

A third-party component saves development cost only if its lifecycle, license, security and operational behavior are acceptable. Dependencies are part of the system and must be versioned and monitored.

Coding style

Style exists to make code consistent and reduce cognitive noise. It should prioritize clarity, correctness and automated formatting/static checks over subjective preference.

Exception/error handling

Errors should be represented at the right abstraction level. Low-level exceptions can be translated into domain errors, but diagnostic context should not be destroyed. Recovery is appropriate only when the current layer has enough information to recover correctly.

Code review

Review should examine invariants, boundary behavior, concurrency, security, tests and maintainability. Automatable formatting/lint issues should be handled by tools so human review can focus on reasoning.

Unit 9: Software Architectures

Architectural approach

Architecture defines system boundaries, data ownership, communication and deployment relationships. Architectural decisions are expensive to reverse when they affect persistence or external contracts.

Client-server

A client requests services from a server. The model says little about physical scale; both can be processes on one host or distributed across networks.

Two-tier and three-tier

A two-tier information system often connects client logic directly to a database/service. Three-tier separates presentation, application/domain logic and data management.

The separation can improve deployability and policy control, but extra tiers add latency and operational complexity.

N-tier and distributed architectures

N-tier systems split responsibilities further. Distribution should follow real scaling, isolation or organizational needs. A network call is slower and less reliable than an in-process call and introduces partial failure.

Web architecture

Web applications combine HTTP, browser/client state, application services, caching and persistent storage. Stateless server endpoints can simplify scaling, but sessions and transactions still create state somewhere in the system.

Historical WAP

WAP-era architectures adapted web-style services to constrained mobile devices and networks. Their historical importance is in showing how protocol stacks respond to bandwidth/device constraints; contemporary mobile/web systems use different standards.

Component-based architecture

Components expose defined interfaces and can be developed/deployed/reused with controlled dependencies. Component boundaries should correspond to cohesive behavior, not arbitrary packaging.

SOA and microservices

Service-oriented architecture organizes capabilities behind service contracts. Microservices emphasize independently deployable bounded services, decentralized ownership and operational automation.

Splitting a monolith into network services does not automatically improve architecture. It introduces distributed transactions, observability, versioning, retries, latency and deployment complexity. A modular monolith is often the better starting point when independent scaling/deployment is not required.

Production Behavior: Architecture Is More Than the Happy Path

An architecture diagram can show services, data stores, and connections, yet the most important production behavior often appears when a dependency slows down, becomes partially unavailable, or approaches saturation. Architectural evaluation should therefore include failure paths and recovery paths, not only the successful request path.

A slow dependency can produce a chain such as:

dependency latency
-> resources held for longer
-> more concurrent in-flight work
-> thread / pool / connection pressure
-> queue growth
-> higher tail latency
-> timeouts
-> retries create additional traffic
-> a local problem becomes systemic

None of these steps is necessarily a defect in isolation. The architectural problem appears when one layer can absorb another layer's failure or capacity behavior without a bound. Timeouts, bounded queues, concurrency limits, load shedding, circuit breakers, and bulkheads are therefore not merely library features; they are mechanisms for limiting a failure domain.

Slowness is also a failure mode

A dependency that rejects a connection is usually obvious. A dependency that still responds but becomes progressively slower can be more dangerous because calls keep resources occupied while the system continues to look nominally available.

Health assessment may therefore need more than is the process running?:

response correctness
+ latency budget
+ dependency saturation
+ queue depth
+ recovery behavior

Isolation is not the same as optimization

Sharing every worker, thread, or connection budget can maximize average utilization, but it can also allow an expensive low-priority workload to consume resources required by a critical path. Separating resource or concurrency budgets may reduce theoretical peak utilization while improving predictability and reducing blast radius.

The trade-off is therefore broader than maximum throughput:

throughput
↔ isolation
↔ latency predictability
↔ recovery time

Steady state is a production requirement

A system is not stable merely because it survives a short benchmark. If queues, temporary files, logs, sessions, cache keys, native buffers, or connections accumulate over time, the system is consuming future capacity.

A stable lifecycle should converge back toward an acceptable operating band:

load arrives
-> system responds
-> load subsides
-> backlog drains
-> temporary resources are released
-> the system returns to its safe operating band

Production-ready architecture therefore treats resource lifecycle and recovery behavior as part of the architectural contract, not as post-deployment cleanup.


From a Modular Monolith to Distributed Services: Moving a Boundary Transfers Cost

The distinction between a monolith and microservices is more than the number of processes used to run an application.

The deeper architectural decision is:

which responsibility
which data
which failure boundary
which deployment boundary
belongs together?

Moving a module from an in-process boundary to a remote service does not remove its business logic. It transfers the boundary and creates a different set of costs.

Modularity and distribution are not the same property

One deployable application can still have strong module boundaries:

application
├── identity
├── orders
├── inventory
└── reporting

Each module can define its responsibility, exposed entry points, internal implementation, and permitted dependencies.

Conversely, a system with dozens of services can remain tightly coupled if every service accesses the same tables directly, must be deployed together, or participates in long synchronous call chains.

Therefore:

many processes ≠ low coupling
one process ≠ high coupling

A process boundary creates a new failure boundary

An in-process call is roughly:

caller
  ↓
function / method
  ↓
return / exception

Moving the call across a network adds serialization, network transport, remote queues or workers, remote dependencies, and a response path.

The design must now address connection failure, lost responses after successful execution, retries with uncertain completion, deadline ownership, version compatibility, partial failure, and end-to-end tail latency.

Turning a method call into a network call adds not only latency but also uncertainty.

Is deployment independence actually being used?

Independent deployment can be a strong reason to create a service boundary.

But if a change to one service requires several others to be deployed in the same release train, physical separation has not created meaningful deployment independence.

True independence requires contracts that can evolve with some degree of backward compatibility.

If every change to one service requires many others to be released at the same time, the architecture may have become a distributed monolith.

Data ownership is central to a service boundary

Code is relatively easy to separate. Data boundaries are harder.

If two services update the same tables directly, ownership of schema, invariants, migration order, lock behavior, and field meaning becomes ambiguous.

A strong service boundary usually combines:

behavior ownership
+
data ownership
+
change ownership

This does not imply a universal rule that every microservice must run on a physically separate database server. The important property is explicit ownership of data changes and an explicit path for crossing the boundary.

Distributing a local transaction changes the shape of the problem

A local database transaction can preserve several state changes inside one atomic boundary.

When those responsibilities become separate services, one local ACID transaction no longer naturally spans the complete workflow.

The architecture must now define what happens when a later step fails, whether an earlier step can be compensated, what happens when a message arrives twice, how temporary unavailability is represented, and whether the workflow is complete or still in progress.

Patterns such as saga, outbox, idempotency, and compensation manage the failure model introduced by distribution. They are not free capabilities obtained by splitting the application.

local transaction
       ↓ distribution
distributed workflow
       ↓
state machine + failure handling

That additional machinery is part of the architectural cost.

A network call makes coupling more expensive

An unnecessary dependency between two modules can remain hidden inside a monolith because an in-process call is cheap.

Moving the same dependency over a network exposes latency, serialization, timeouts, retries, connection management, observability, versioning, and partial failure.

This does not make microservices inherently poor architecture. If a process boundary serves a real scaling, isolation, organizational, or security requirement, the cost can be justified.

Distribution simply does not automatically repair bad module boundaries.

Fan-out can amplify tail latency

Suppose one user request synchronously depends on several services. The response often cannot complete until the slowest required dependency finishes.

Even when each dependency is only occasionally slow, increasing fan-out increases the probability that the user encounters at least one slow dependency.

Average service latency is therefore insufficient. End-to-end P95, P99, timeout budgets, fan-out, and retry amplification must be considered together.

The quantitative performance model is developed further in High-Performance Java Data Systems.

A retry is a mechanism that creates new traffic

Retrying a remote failure may improve reliability.

However, when overload causes the timeout, a positive feedback loop is possible:

overload
  ↓
timeout
  ↓
retry
  ↓
more load
  ↓
more timeout

A distributed architecture therefore makes timeout, retry, backoff, jitter, idempotency, and load-shedding policies part of architecture rather than implementation detail.

The operational tax

An independently deployed service carries more than its business logic.

Builds, artifacts, configuration, secrets, deployment, health, logging, metrics, tracing, alerting, capacity, backup and recovery, security updates, and version compatibility all become part of its lifecycle.

Ten services do not merely mean ten collections of source code. They can mean ten operational lifecycles.

Automation can reduce this cost substantially, but it cannot make the cost disappear.

Organizational boundaries matter as much as technical ones

If two services are always changed by the same team, always released together, and each contains part of the same business rule, the split may not represent a natural boundary.

A service boundary can be more valuable where different areas have different rates of change, scaling requirements, security boundaries, or release lifecycles.

Architecture is not only a topology of CPU and memory. It also shapes the topology through which people create change.

A modular monolith does not have to be temporary

A modular monolith is sometimes described only as an intermediate stage before microservices. That is not a requirement.

If the system can be safely managed as one deployment, has independently understandable modules, explicit data boundaries, fits within one deployment model, and does not require independent team deployment, a modular monolith can remain a durable architecture.

A separate service becomes valuable when it solves a new requirement.

Stronger signals for extracting a service

Useful reasons for moving a module behind a service boundary include independent scaling, independent deployment, physical fault isolation, a distinct security or trust boundary, a genuinely different runtime requirement, natural team ownership, or independent data and invariant ownership.

Weaker justifications include adopting microservices because they are modern, because the system might become large someday, or because every domain noun appears to deserve its own service.

Test logical independence before physical distribution

A module can be tested for logical independence before it moves outside the process.

Package visibility, architecture tests, or module-verification tooling can protect module APIs and dependency direction.

In the Spring ecosystem, Spring Modulith can model application modules, their exposed interfaces, and inter-module dependencies and can verify structural constraints.

The important part is not the specific tool.

The durable engineering question is:

does the logical boundary work
before a network boundary is added?

Moving a poor logical boundary into another process only makes the boundary more expensive.

Controlled extraction instead of a broad rewrite

Splitting a proven large application into services in one operation changes business behavior and deployment/failure topology at the same time.

A more controlled sequence is:

measure existing behavior
    ↓
identify the module boundary
    ↓
reduce internal coupling
    ↓
establish a contract
    ↓
move the boundary out of process only if justified
    ↓
verify equivalent required behavior

This follows the same principle as maintenance engineering: modernization should solve a real problem through controlled change rather than recreate proven behavior for architectural fashion.

Final principle

The useful question is not which architecture is more modern.

It is:

which boundary do we need to move
outside the process,
which concrete problem does that solve,
and which new failure and operational
costs are we accepting in return?

A distributed architecture does not eliminate complexity. It moves complexity.

Unit 10: CASE, Toolchains and Reengineering

CASE concept

CASE tools historically supported modeling, documentation, code generation and project management. The objective was consistency among engineering artifacts.

Tool integration

Tools add value when information flows between them reliably. A diagramming tool that is never updated after implementation quickly becomes misleading.

Modern engineering toolchain

A contemporary toolchain may integrate:

issue/requirement tracking
   ↓
source control
   ↓
code review
   ↓
CI build + tests + static analysis
   ↓
artifact repository
   ↓
deployment automation
   ↓
monitoring / incident feedback

Traceability can be maintained through identifiers and immutable build metadata rather than manual duplication.

Selecting tools

Tool choice should consider interoperability, automation, exportability, vendor lock-in, learning cost, security and whether the tool supports the actual engineering process.

Reverse engineering

Reverse engineering recovers design, interfaces or behavior from an existing system. It is necessary when documentation is absent, for migration, compatibility, security analysis and legacy modernization.

Reengineering

Reengineering improves an existing system while preserving required external behavior. It can include refactoring, architecture changes, data migration, language/platform migration and replacement of obsolete components.

A safe reengineering process establishes behavioral baselines and migration tests before changing internals. Otherwise "modernization" can destroy undocumented but required behavior.

Overall Framework

The course forms a feedback system rather than a one-way sequence:

requirements
    ↓
design / architecture
    ↓
implementation
    ↓
verification
    ↓
operation
    ↓
observed failures and new needs
    └──────────────→ requirements/design

Project management controls resources and risk around this loop. Configuration management preserves reproducibility. Quality and testing supply evidence. Maintenance returns operational knowledge to the next design decision.

The central engineering lesson is that software quality is an emergent property of the complete lifecycle. Correct source code cannot compensate for an incorrect requirement, an unsafe deployment, an uncontrolled data migration or an architecture whose failure modes were never designed.

Separate Deep Dives for Security and Change

This course keeps process, requirements, design and quality in the general software-engineering frame. For threat modeling, trust boundaries, secure development and verification, see Secure Software Engineering. For dependency structure and change resilience, see Change-Resilient Architecture with SOLID.

Change Engineering in Proven Large Systems

Designing a new system and changing a system that has worked for years are different engineering problems. Greenfield development can emphasize architectural ideals; in a proven production system, observed behavior, user habits, data reality, and operational procedures are also part of the contract.

The first question in modernization is therefore not “how would I design this from scratch today?” but “which behavior must be preserved, why, and which real problem am I solving?” Rebuilding a working system merely to use a newer framework, shorter code, or a more symmetrical package structure can increase change risk without improving the operational outcome.

Safe change follows a few durable principles:

verify current behavior from source
bound the affected path
make the smallest coherent change
preserve backward compatibility
verify according to risk
harvest durable new rules into project context

“Old” and “wrong” must remain separate concepts. A schema limitation, historical duplicate data, a familiar keyboard behavior, or an established integration path may differ from an idealized design, but once it becomes a production contract, changing it requires a separate requirement and verification effort. A proven defect, on the other hand, should not be preserved merely because it is legacy.

Staged modernization can therefore be stronger than a large rewrite. Observable and recoverable boundaries are selected first, behavioral equivalence is demonstrated, and only then does the next stage begin. Each stage has its own acceptance criteria and residual risk. From the perspective of Software Test Engineering, evidence is produced at the same cadence as the change.

The interface is part of this contract as well. In a heavily used business application, panel ordering, keyboard shortcuts, and record-selection behavior are more than visual preferences; they can become muscle memory. Such changes should be treated as UX regressions under UI/UX Engineering in Web Applications.

The goal of maintenance engineering is not to erase every historical trace. It is to make the reasons for behavior visible, remove unnecessary complexity, and keep the impact surface of each new change small. This connects process engineering directly with Clean Code and Maintainability Engineering.

  • Spring Modulith Reference: https://docs.spring.io/spring-modulith/reference/
  • Martin Fowler, Microservices: https://martinfowler.com/articles/microservices.html
  • Martin Fowler, MonolithFirst: https://martinfowler.com/bliki/MonolithFirst.html
  • Sam Newman, Building Microservices, 2nd ed., O'Reilly Media, 2021

Behavioral inventory and the evidence chain of a change

Before modifying a critical system, the running behavior should be inventoried alongside written requirements. Source code, user workflow, data reality, and operating rules may have drifted apart over time. Relying on documentation alone or on source alone can therefore produce incomplete evidence.

A practical change chain can be expressed as follows:

observed behavior
      |
      v
invariant to preserve
      |
      v
change boundary
      |
      v
verification evidence
      |
      v
new durable rule

Every change should separate three sets: behavior that must remain unchanged, behavior that is intentionally being changed, and behavior that is still uncertain. The uncertain set should be reduced before implementation; otherwise the developer can redefine an existing contract accidentally.

The change boundary is not merely a file list. A data-write path can affect other views of the same entity; a client-lifecycle change can alter automatic refresh and keyboard focus; a concurrency limit can change both latency and failure behavior. Impact analysis therefore needs to follow call paths, data ownership, and user tasks.

Evidence should be proportional to risk. A local visual adjustment may need only a narrow smoke check. A change in data integrity, authorization, or concurrency may require compilation, contract verification, negative scenarios, and an end-to-end workflow together. Software Test Engineering develops these evidence layers further.

The final step is to move only genuinely durable information into project context. Turning every implementation detail into a permanent rule bloats the context, while failing to record a high-impact invariant forces the same investigation to be repeated. Documentation then becomes more than a post-change summary: it narrows the decision surface of the next change.

Software development life cycle

The Software Development Life Cycle (SDLC) organizes activities from requirements and planning through design, implementation, verification, deployment, operation, and maintenance. Waterfall, incremental, iterative, agile, and DevOps-oriented processes arrange these activities with different sequencing, feedback frequency, and delivery cadence. SDLC is therefore not synonymous with one process model; it is the broader lifecycle through which a need becomes an operable and maintainable software system.

Traceability from Requirement to Deployment

A durable software-engineering problem, independent of process methodology, is being able to explain why a change exists and what technical evidence supports it. Traceable links among requirements, design decisions, code changes, tests, and releases are especially valuable in critical systems and post-incident analysis.

This does not require a heavyweight document chain in every project. Existing engineering artifacts such as issue IDs, architecture decision records (ADRs), commits, test results, and release records can provide sufficient traceability when linked consistently.

The proper role of design patterns

Patterns such as Factory, Strategy, Observer, Adapter, Facade, and Singleton give names to recurring design problems. Using a pattern is not itself evidence of quality. Introducing a pattern before the problem exists can increase class count, indirection, and cognitive load.

Strategy can make a changing algorithm selectable behind a contract; Adapter can translate incompatible interfaces; Observer can model publication to multiple subscribers. Singleton simplifies global access but can increase hidden coupling and test-isolation cost. Patterns should be selected for an explicit trade-off.

Git and version control

A commit in distributed version control is more than a file backup; it is a unit of change history. Small, meaningful, buildable commits reduce review and rollback cost. Branching strategy should match the team's release model; there is no universal branching scheme.

A merge conflict does not necessarily mean either developer worked incorrectly. It means the same region evolved on different histories. Correct resolution must preserve business semantics rather than merely produce syntactically compilable text.

Git and Apache Subversion (SVN) solve version control with different repository models. A Git working clone contains repository history and supports local commits in a distributed model, whereas SVN traditionally uses a central repository with working copies. That distinction affects offline work, branching and merging, access control, and strategies for large binary assets. Tool selection should follow repository size, team workflow, authorization, and integration requirements rather than a simple “newer is better” rule.

CI/CD and automation

Continuous Integration emphasizes frequent integration with automated verification. Continuous Delivery keeps software in a deployable state; Continuous Deployment automatically releases successful changes to production. Delivery and deployment are not synonyms.

A pipeline containing many tools is not a quality guarantee. Fast reliable feedback, deterministic builds, necessary tests, security controls, and controlled artifact production matter more than tool count.

Monoliths, modular monoliths, and microservices

A monolith is not inherently bad architecture, and a microservice is not inherently good architecture. Microservices can enable independent deployment and scaling, but introduce network failure, distributed consistency, observability, versioning, and operational overhead. A well-bounded modular monolith can be the lower-complexity solution for many systems.

Service boundaries should be justified through domain boundaries, team ownership, independent change/scale needs, and data ownership rather than technology fashion.

Bounding change risk

In a proven production system, refactoring should not rewrite behavior merely to make code look cleaner. Characterize current behavior, narrow the change boundary, identify dependent contracts, and preserve rollback. For critical paths, small, observable, reversible changes generally carry less operational risk than a broad one-shot rewrite.

Architecture decisions and change risk

Architecture is a history of decisions, not only a diagram. When rationale disappears, teams repeat old debates or reverse constraints they no longer understand. A lightweight Architecture Decision Record can preserve context, alternatives, the chosen option, expected consequences, and conditions for reconsideration.

Change risk depends more on dependency direction than on line count. A small change to a shared contract can affect many consumers, while a large isolated change may remain local. Review and testing should therefore focus on blast radius rather than diff size alone.

Keeping engineering decisions traceable

A requirement becomes more useful when it is connected to the user need it serves and to evidence that verifies it. Traceability from requirement to architecture decision, implementation change, and test result makes change impact easier to assess.

Architecture decision records should capture context, alternatives, the selected approach, and conditions for revisiting the decision. They do not declare one architecture universally correct; they preserve the assumptions behind the choice.

Quality metrics are contextual. Coverage, defect counts, and lead time do not prove quality in isolation. The decision supported by a metric and the behavior it may incentivize should remain explicit.

Software Engineering for Learning Systems

In an AI-enabled system, the model is only one architectural component. Requirements, data, model artifacts, runtimes, service boundaries, and human validation must be engineered together. The connection between software engineering and AI therefore appears primarily in managing behavior and change, not in the model-training algorithm itself.

In conventional software, a large part of behavior is determined by source code and configuration. In a learning system, data and learned parameters also enter the state:

output = f(code, configuration, data, model, runtime)

This broadens versioning. A source commit alone may be insufficient to reproduce a result; the dataset slice, label version, feature pipeline, model artifact, runtime, and evaluation set may also need stable identities.

Requirements must also be more explicit than “achieve high accuracy.” Relevant operating distribution, false-positive and false-negative costs, latency limit, memory budget, fallback behavior, and mandatory human-review conditions should be stated separately. Without those constraints, a model metric cannot be traced to a system requirement.

Architecture should keep model interaction behind an explicit contract rather than scattering model calls throughout the codebase. Input validation, timeout policy, error handling, and model version can then be controlled at one boundary. If inference is remote or out-of-process, network failure, capacity exhaustion, and partial failure become ordinary system states.

business rule
↓
model contract
↓
model / inference runtime
↓
measured output
↓
application decision

A model score and a business decision need not be identical. The application may combine the score with thresholds, authorization, or contextual rules. This separation improves testability and change control.

The data path is part of the software life cycle as well. Divergence between training preprocessing and production preprocessing creates training-serving skew. Missing-value policy, scaling, encoding, and feature semantics must therefore be versioned and tested as executable contracts.

A model update should be treated as an independent release event. The API may remain unchanged while the error distribution changes. Comparative evaluation, shadow or canary deployment, and a rollback path can be necessary. Rollback may need to restore the model and its associated preprocessing contract together.

AI-assisted code generation does not remove these responsibilities. Generated code still needs requirements, dependency and license review, security analysis, and regression validation. “The model suggested it” is not an engineering rationale.

The underlying principle remains familiar: make every behavior-defining input visible, define boundaries explicitly, trace change, and validate the result against requirements. A learned model introduces a new class of artifact; it does not replace software-engineering discipline.

AI Engineering: Turning a Probabilistic Component into a Software System

AI engineering is not the same as using a chat model to help write code. Turning a model into a reliable production component requires its own lifecycle.

Problem
  |
Data / Context
  |
Model + Prompt + Tools
  |
Evaluation
  |
Serving
  |
Observation
  |
Feedback / Regression

A successful API call does not mean the feature is production-ready.

Testing Probabilistic Components

Traditional functions often support exact deterministic assertions.

LLM components may instead require contracts around required fields, forbidden claims, citation use, schema validity, and minimum task quality.

A stable evaluation set should contain normal examples, boundary cases, historical failures, adversarial inputs, and no-answer cases.

feature change
  |
unit/integration tests
  +
AI evaluation suite
  |
release gate

AI evaluations complement rather than replace conventional software tests.

Version Model, Prompt, Data, and Tools Together

The same code can behave differently after a model, prompt, embedding, or retrieval change.

AI_RELEASE =
model
+ tokenizer
+ prompt
+ embedding
+ retrieval config
+ tool schema
+ evaluation dataset

Production provenance should make it possible to identify which model, prompt, retrieval version, and tool output produced a result.

Testing Non-Deterministic Output

With sampling enabled, exact string equality can be the wrong assertion. Tests can instead check structural invariants and task-level rates:

is JSON valid?
are required sources present?
was a forbidden field generated?
what is success over 100 cases?

Deterministic settings remain useful when they are compatible with the product because they simplify regression analysis.

Canary, Shadow, and Progressive Delivery

A new model should not automatically receive all traffic.

Shadow deployment observes real inputs without changing user-visible decisions.

Canary deployment sends a small traffic share to the new version.

Measure quality, tail latency, error rate, compute cost, abstention rate, and safety regressions.

Rollback Is More Than Reverting a Model File

Behavior can change because of prompts, embeddings, retrieval parameters, tools, or policy. A rollback should restore a compatible bundle of these artifacts.

AI Engineering vs AI-Assisted Development

AI-assisted software development uses AI to help produce code, tests, documentation, or analysis.

AI engineering designs models, data, evaluations, serving, security, observability, and lifecycle as a production capability.

coding with AI
   !=
engineering an AI system

Intent-Driven Programming develops specification and verification at the developer-agent boundary, while Large Language Models and RAG cover model and retrieval layers.

AI Technical Debt

The concept of hidden technical debt in machine-learning systems becomes broader in generative-AI products.

Sources include model-provider coupling, prompt accumulation, untested data changes, retrieval-index drift, tool-schema incompatibility, and unobserved quality regression.

A one-line model configuration change can therefore represent a large system change. Software-engineering principles still apply: boundaries, dependencies, tests, provenance, and rollback must remain explicit.

References

  • Apache Software Foundation. Subversion Documentation. https://subversion.apache.org/docs/
  • D. Sculley et al. “Hidden Technical Debt in Machine Learning Systems.” Advances in Neural Information Processing Systems, 28, 2015.
  • Git Project. Git Documentation. https://git-scm.com/docs
  • Ian Sommerville. Software Engineering. Addison-Wesley, 2010.
  • IEEE Computer Society. Guide to the Software Engineering Body of Knowledge, Version 3.0. IEEE Computer Society, 2014.
  • ISO/IEC. ISO/IEC 25010:2011 Systems and software quality models. International Organization for Standardization, 2011.
  • ISO/IEC/IEEE. ISO/IEC/IEEE 29148:2011 Requirements engineering. ISO / IEC / IEEE, 2011.
  • Michael T. Nygard. Release It!: Design and Deploy Production-Ready Software. 2nd ed. Pragmatic Bookshelf, 2018.
  • Nancy G. Leveson. Engineering a Safer World: Systems Thinking Applied to Safety. MIT Press, 2011.
  • National Institute of Standards and Technology. Artificial Intelligence Risk Management Framework: Generative Artificial Intelligence Profile (NIST AI 600-1). 2024. DOI: https://doi.org/10.6028/NIST.AI.600-1
Contents
QR code for this page