How We Got Here

Decades of software engineering practice provide the architecture, shared logic, and persistent project instructions needed to guide AI coding agents.

Software Engineering in the Age of AI — Part 2: Architecture, Methodology, and Agent Instructions

In the first article of this series, Before the First Prompt, we established the foundation for developing software with AI coding agents. We created a development environment, introduced source control and automated testing, and established the separation between development and production through a controlled CI/CD process. None of those practices originated with artificial intelligence. They are the result of decades of experience building and maintaining software systems.

With that foundation in place, we’re ready to consider how the software itself should be engineered. Before we assign an AI agent its first development task, we need to establish the application’s architecture, determine which engineering practices we’ll follow, and provide instructions that allow the agent to work consistently within those decisions.

This raises an interesting question. If AI coding agents represent such a fundamental change in how software is produced, do we need an entirely new development methodology to accommodate them? I would argue that the answer is largely no. We already have an extensive body of knowledge about requirements analysis, software architecture, iterative development, testing, and managing change. The challenge is applying those lessons to autonomous implementation rather than allowing each agent to invent its own approach.

To understand how we might accomplish that, it’s worth examining the evolution of software development methodologies and, more importantly, why those methodologies developed in the first place.

From Software Development to Software Engineering

In the early years of commercial computing, software development was often treated as an extension of programming. Applications were smaller, computing resources were expensive, and the people developing software frequently worked closely with the people who would operate it. As systems became larger and organizations increasingly depended on them, the limitations of informal development practices became apparent.

Large software projects required coordinated teams, documented requirements, predictable schedules, and methods for controlling changes. The difficulty was no longer simply translating an algorithm into executable instructions. Engineers needed ways to manage the relationships between requirements, components, interfaces, data, testing, and deployment.

Structured analysis and structured design emerged as approaches to managing that complexity. Functional decomposition allowed large problems to be divided into smaller, more manageable components. Data-flow diagrams described the movement and transformation of information, while modular programming encouraged developers to separate responsibilities rather than constructing applications as collections of tightly coupled procedures.

These approaches introduced principles that remain relevant today. A system should be understandable through its constituent parts, responsibilities should be clearly assigned, and interactions between components should be defined rather than improvised. Those principles would eventually reappear in object-oriented design, component-based development, service-oriented architecture, and modern distributed systems.

The challenge was organizing the development lifecycle around those engineering activities. As projects grew, organizations needed a predictable sequence for requirements analysis, design, implementation, testing, and deployment. This need helped establish the staged development approaches that would become associated with the Waterfall model.

Waterfall and the Search for Predictability

The traditional Waterfall model divides software development into sequential phases. Requirements are established before design, design precedes implementation, and implementation is followed by testing and deployment. The approach offers a clear management structure, with identifiable deliverables and approval points between phases.

For organizations accustomed to managing construction and other engineering projects, this sequence was attractive. It appeared to provide a way to estimate costs, establish schedules, and control delivery through documented milestones. It also encouraged serious consideration of requirements and architecture before implementation began.

The limitations became evident when requirements changed or important assumptions proved incorrect. Software is an unusual engineering product because the full implications of a requirement are frequently discovered during implementation. Users may struggle to articulate what they need until they interact with a working application, and technical constraints may not become apparent until engineers attempt to implement a design.

When testing is concentrated near the end of a long development cycle, defects introduced much earlier can become expensive to diagnose and correct. The longer the interval between making a mistake and discovering it, the greater the opportunity for subsequent work to depend on that mistake.

An interesting historical detail is that Winston Royce’s influential 1970 paper, frequently associated with Waterfall, actually discussed the risks of a purely sequential approach and recommended feedback, preliminary design, and iteration. The simplified version that became widely recognized as Waterfall did not fully reflect the qualifications in his original discussion.

The lesson was not that planning and documentation were unnecessary. It was that software development needed a more effective way to incorporate feedback and accommodate new information without abandoning engineering discipline.

Iterative Development and the Rational Unified Process

During the 1980s and 1990s, iterative and incremental development became increasingly important. Rather than attempting to complete an entire application through one sequence of development phases, teams could construct and evaluate smaller portions of the system, incorporating what they learned into subsequent iterations.

Barry Boehm’s Spiral model, published in the 1980s, emphasized managing risk through repeated cycles of planning, analysis, development, and evaluation. Instead of treating all requirements and implementation decisions as equally certain, development activities could be organized around the risks most likely to threaten the project.

The Rational Unified Process, or RUP, emerged during the 1990s as a comprehensive framework for iterative software engineering. Closely associated with object-oriented analysis and design, UML, and Rational Software’s development tools, RUP combined iterative development with a strong emphasis on architecture and requirements.

Three characteristics of RUP are particularly relevant to our discussion.

First, it was use-case-driven. Requirements were expressed in terms of interactions between users and the system, helping connect technical implementation to business behavior.

Second, it was architecture-centric. Establishing the important structural decisions early helped ensure that subsequent development took place within a coherent system design.

Third, it was iterative and incremental. Development proceeded through iterations within broader lifecycle phases, allowing teams to evaluate progress, address risks, and refine the implementation.

RUP also defined roles, activities, artifacts, and workflows. Requirements models, architectural descriptions, implementation components, and test artifacts provided a structured way to communicate engineering decisions across a development team.

One criticism of RUP was that organizations could implement it with excessive process overhead. A methodology intended to be configurable could become an elaborate collection of required documents, approvals, and procedures. The result sometimes placed more emphasis on producing artifacts than on delivering working software.

Nevertheless, the underlying ideas remain valuable. Architecture should guide implementation, requirements should be traceable to delivered functionality, and engineering decisions should be documented sufficiently for others to understand and maintain the system.

For AI-assisted development, these ideas have immediate applications. An agent needs architectural context, clear requirements, and established boundaries just as a human developer does. Without those things, the speed of implementation can amplify inconsistencies rather than improve productivity.

Extreme Programming and the Engineering of Rapid Feedback

At roughly the same time RUP was developing, another approach was emerging from a very different direction. Extreme Programming, commonly called XP, grew out of Kent Beck’s work and the Chrysler Comprehensive Compensation project during the 1990s.

XP was not simply a lighter version of RUP, nor did it evolve directly from it. The two approaches developed largely in parallel, responding to many of the same software engineering problems while emphasizing different solutions.

Where RUP provided a broad lifecycle framework with defined activities and artifacts, XP concentrated on the engineering practices necessary to support rapid, reliable change. Its practices included test-first development, continuous integration, refactoring, pair programming, simple design, collective code ownership, and frequent releases.

The underlying principle was that software quality should not depend on a lengthy verification phase at the end of development. Instead, verification should occur continuously as the software is constructed.

Test-Driven Development became one of XP’s defining practices. Developers would establish expected behavior through automated tests before implementing functionality, then refactor the resulting code while maintaining those tests. Continuous integration reinforced the approach by detecting conflicts and regressions as changes entered the shared codebase.

What made XP particularly interesting was its recognition that development speed and engineering discipline were not necessarily opposing forces. Automated testing, refactoring, and continuous integration required effort, but that investment made frequent changes safer and reduced the cost of correcting defects.

This is precisely what I experienced on the Bosch project described in the first article. The team wasn’t simply following a schedule of development activities. The engineering practices were built into the daily workflow, and the CI/CD infrastructure supplied continuous evidence about the state of the application.

For today’s AI coding agents, XP may be one of the most directly applicable developments in the history of software engineering. An agent can execute a short implementation cycle, run automated tests, inspect failures, and adjust its work repeatedly. The faster the agent can produce changes, the more valuable that immediate feedback becomes.

However, XP also reminds us that small implementation cycles depend on clear responsibilities and good design. Rapid iteration does not eliminate the need for architecture, and passing unit tests does not necessarily mean that independently developed components will form a coherent application.

That brings us to the movement that would combine many of these ideas under a common philosophy.

Agile: A Philosophy Becomes an Industry

In February 2001, seventeen software development practitioners met in Snowbird, Utah, and produced the Manifesto for Agile Software Development. The participants represented several approaches, including XP, Scrum, Crystal, and other lightweight development methods.

The Manifesto did not establish a single development methodology. It articulated common values that emphasized collaboration, working software, and the ability to respond to change. Its accompanying principles also emphasized frequent delivery, sustainable development, good design, and technical excellence.

This distinction matters because Agile was never intended to mean abandoning software engineering discipline. The original principles explicitly recognized that technical excellence and good design improve a team’s ability to respond to changing requirements.

As Agile became increasingly popular, however, its implementation varied considerably. Organizations adopted Scrum, sprint planning, product backlogs, daily standups, retrospectives, and other mechanisms for organizing development work. Many of these practices improved communication and gave stakeholders greater visibility into project progress.

The problem was that adopting a framework for managing development did not necessarily mean adopting the engineering practices required to deliver reliable software.

Scrum, for example, deliberately does not prescribe a particular programming language, architectural approach, testing methodology, or continuous integration solution. It provides a framework for organizing work and inspecting progress, leaving teams to select the engineering practices appropriate to their circumstances.

That flexibility can be an advantage, but it also creates an opportunity for incomplete adoption. An organization can conduct sprint planning, hold daily standups, measure velocity, and demonstrate software every two weeks without consistently practicing TDD, maintaining a coherent architecture, or implementing continuous integration.

In such an organization, the development process may look Agile while the underlying engineering practices remain largely unchanged.

I don’t believe this was an inevitable consequence of Agile, nor should it be interpreted as a criticism of Scrum itself. The distinction is between managing the flow of work and engineering the software being delivered. Both matter, but they solve different problems.

The lesson becomes particularly important when AI agents enter the development team. A coding agent doesn’t need a daily standup to remind it to write tests. It needs an explicit engineering requirement, access to the necessary tools, and a verification process that determines whether the requirement has been satisfied.

DevOps and the Completion of the Feedback Loop

As iterative and Agile development became established, organizations increasingly confronted another problem. Software could be developed and tested in short iterations, but deploying it into production remained a separate, often difficult activity.

Development teams and operations teams frequently worked under different priorities. Developers wanted to deliver changes rapidly, while operations teams were responsible for maintaining stability and controlling risk. Manual deployment processes, environment inconsistencies, and organizational boundaries could delay releases even when implementation work was complete.

The DevOps movement developed around improving collaboration between development and operations while increasing automation throughout the delivery process. Continuous integration evolved alongside continuous delivery, infrastructure as code, automated deployment, monitoring, and operational feedback.

The objective was to make software delivery a repeatable engineering activity rather than a collection of manual procedures performed at the end of development.

For our purposes, DevOps completes an important part of the lifecycle. TDD provides feedback about individual behaviors, integration testing evaluates how components work together, and CI/CD verifies and promotes changes through controlled environments. Monitoring and operational feedback then provide information about how the software behaves after deployment.

This is the development foundation we established in the first article, and it remains relevant regardless of whether the implementation is produced by humans or AI agents.

What changes with autonomous development is the amount of work that can occur between those feedback points. An agent can make a substantial number of changes in a short period, making rapid automated verification and controlled integration even more important.

The lesson of DevOps is not simply that deployments should be automated. It is that software development and software operation belong to one continuous engineering lifecycle.

What We Should Carry Forward

Looking back across these methodologies, I don’t see a succession of ideas in which each new approach makes its predecessors obsolete. I see an accumulation of engineering knowledge, sometimes accompanied by changes in emphasis and occasional overcorrections.

Structured development taught us the importance of requirements analysis and functional decomposition. Iterative development demonstrated the value of feedback and risk reduction. RUP emphasized architecture, use cases, and traceable engineering artifacts. XP made testing, refactoring, and continuous integration central to implementation. Agile encouraged collaboration and adaptation, while DevOps extended engineering discipline through deployment and operation.

These practices are complementary when applied appropriately. We don’t need to choose between architecture and iteration, or between testing and rapid delivery. We need to establish a development process that combines the practices required by the system we’re building.

This becomes more important as AI reduces the cost of generating implementations. When we can produce functional code quickly, the temptation is to allow each application or feature to develop independently. That may deliver an impressive demonstration, but it creates problems when the system must be maintained or extended.

Before we define how an AI agent should work, we therefore need to address one more foundational principle: software architecture and the deliberate reuse of common functionality.

Architecture Is Not an Implementation Detail

One of my longstanding frustrations with software development, particularly in game development and rapid application prototyping, is the tendency to build applications as self-contained implementations without giving sufficient consideration to reusable business logic.

A game engine may provide sophisticated rendering, physics, asset management, and other shared services, yet much of the game’s domain logic can still become tightly coupled to engine-specific objects and interfaces. Inventory management, transaction rules, character progression, or simulation behavior may be implemented directly inside gameplay components, making that functionality difficult to reuse elsewhere.

The same problem can occur when AI agents generate applications independently. If an agent is asked to create a graphics editor, a page-layout application, and a video editor as separate projects, it may independently implement file handling, color conversion, asset management, state tracking, and other capabilities that could reasonably be shared.

Not all of those applications should use the same underlying implementation. Their domain requirements differ, and forcing unrelated functionality into a common library can create as many problems as duplication. Nevertheless, genuinely shared capabilities should be identified and implemented through well-defined components rather than repeatedly recreated.

This is one of the reasons n-tier architectures became so valuable in enterprise development.

By separating presentation, business logic, and data access, we establish boundaries between what an application displays, how its rules operate, and how its information is stored. The specific technologies may change, and modern architectures may express these separations differently, but the principle remains useful.

Consider our Task Management application. The rule determining whether a completed task can be reassigned is a business rule. It should not be buried inside a web page or duplicated in multiple API controllers. Similarly, the rules for task ownership, valid status transitions, and assignment eligibility should be implemented in an appropriate domain or business layer.

This allows those rules to be tested independently of the interface and, where appropriate, reused by other application interfaces. A web application, a mobile client, and a background process should not need three different implementations of the same business behavior.

Architecture also improves the way we direct coding agents. When component responsibilities are clearly established, an implementation assignment can identify the appropriate layer, interfaces, and dependencies. The agent has less reason to invent its own structural approach, and the resulting changes can be evaluated against an established design.

Our objective is not to create the most elaborate architecture possible. It is to establish sufficient separation of concerns that the application can evolve without becoming a collection of tightly coupled implementations.

Establishing the Architecture of Our Example Application

We now have the development environment from Article 1 and a set of engineering principles drawn from decades of software development experience. The next step is to apply them to our Task Management application.

For this example, we’ll use a conventional layered architecture with separate presentation, business, and data responsibilities. The application will be modest, but we want its structure to illustrate how the same principles can support larger systems.

The presentation layer will expose the application’s user interface and HTTP endpoints. Its responsibility is to handle user interaction and communicate requests and results, not to implement the underlying business rules.

The business layer will contain the application’s domain models, validation rules, task lifecycle behavior, and assignment logic. This is where the meaning of a task, its valid states, and the rules governing its behavior will be defined.

The data layer will manage persistence through SQL Server, including the implementation of repository interfaces defined at the application or domain boundary. Business logic should not depend directly on database-specific details where those details can be isolated behind a clear contract.

A possible solution structure might look like this:

task-manager/
├── .task-manager/
├── docs/
│   └── engineering/
├── src/
│   ├── TaskManager.Web/
│   ├── TaskManager.Application/
│   ├── TaskManager.Domain/
│   └── TaskManager.Infrastructure/
├── tests/
│   ├── TaskManager.Domain.Tests/
│   ├── TaskManager.Application.Tests/
│   └── TaskManager.Integration.Tests/
└── TaskManager.sln

This is a starting architecture rather than an attempt to prescribe the structure of every .NET application. For a small project, some layers may initially contain very little code. That’s acceptable as long as we don’t add abstractions simply to satisfy a diagram.

The important architectural decisions concern dependency direction and responsibility. The domain layer should not need to know which web framework presents its functionality or which database stores its information. Application services coordinate business operations, while infrastructure components implement interfaces for persistence and other external dependencies.

Tests should reflect these boundaries. Domain rules can be tested without launching a web server or connecting to SQL Server, while integration tests verify that the infrastructure implementations behave correctly with the rest of the application.

With the architecture established, we have answered where functionality belongs and how the major components should interact. We still need a way to ensure that AI coding agents consistently follow those decisions.

That requires turning our engineering methodology into something more concrete than a collection of recommendations.

From Engineering Methodology to Agent Instructions

Throughout the history we’ve just examined, software engineering methodologies have depended heavily on human interpretation and adherence. Organizations established standards, published development procedures, documented architectures, and trained developers to follow approved practices.

The problem was rarely a complete absence of engineering knowledge. It was applying that knowledge consistently across projects, teams, and individual development activities.

AI coding agents give us an opportunity to approach this problem differently. Instead of relying entirely on a developer to remember the applicable standards, we can supply an agent with a persistent set of project instructions that identify the architecture, engineering requirements, and development procedures it is expected to follow.

These instructions are not a replacement for automated verification or engineering judgment. An agent may misunderstand an instruction, overlook a dependency, or produce an implementation that violates an architectural boundary. The instructions establish expectations, while tests, code analysis, integration controls, and review provide evidence of compliance.

The opportunity is to make our engineering expectations explicit and reusable. Rather than restating architectural requirements in every new conversation with an agent, we maintain them as version-controlled project assets.

I refer to this approach as the Project Instruction Convention.

The Project Instruction Convention

The concept is straightforward. We establish a hidden directory within the repository, named for the project or organization, that serves as the entry point to its engineering instructions.

For our Task Management application, we’ll use .task-manager. In a larger organization, a common instruction package might instead use a directory named for the organization, with individual projects providing additional instructions specific to their architecture and business requirements.

The directory name is a convention, not a special capability of the AI model. It provides a recognizable location for persistent project guidance, while remaining independent of any particular inference provider or coding agent.

For our example, we might begin with the following structure:

task-manager/
├── AGENTS.md
├── .task-manager/
│   ├── README.md
│   ├── instructions.md
│   ├── methodology.md
│   └── project-context.md
├── docs/
│   └── engineering/
│       ├── architecture.md
│       ├── development-standards.md
│       ├── testing-standards.md
│       ├── development-process.md
│       └── decisions/
├── src/
├── tests/
└── TaskManager.sln

The .task-manager directory identifies the project’s governing context and points to the authoritative documentation. Detailed engineering material remains under docs/engineering, where it can be maintained as ordinary project documentation and reviewed independently of the agent.

This distinction helps prevent the instruction directory from becoming an enormous collection of material that an agent must read indiscriminately. The entry point should tell the agent what the project is, what rules govern its work, and where to find the detailed information relevant to a particular assignment.

The root AGENTS.md file provides a practical compatibility mechanism for coding agents that recognize that convention. Other agents may require a different entry point or configuration file. Those adapters can direct the agent to the same canonical project instructions without duplicating the underlying methodology.

Simply creating a hidden directory does not guarantee that an agent will discover or obey its contents. The execution environment must explicitly load or reference the instructions, and the development workflow must verify their application.

Establishing the Engineering Rules

We should now populate the instruction framework with the decisions derived from the methodologies discussed earlier.

The project methodology document will establish the general development approach. It will require small, independently verifiable implementation changes, test-first development where appropriate, regular integration, and corrective refactoring rather than accumulating temporary patches.

The architectural documentation will establish the responsibilities of each layer, permitted dependency directions, interface conventions, and expectations for reusable components. It should also specify how new functionality is introduced without duplicating existing business rules or allowing presentation code to become responsible for domain behavior.

Development standards will describe code organization, naming conventions, dependency management, error handling, logging, and other implementation expectations. Testing standards will identify the types of automated tests required, how they are organized, and the conditions under which an implementation is considered verified.

Finally, the development process will connect agent activity to the repository and CI/CD pipeline established in the first article. It will describe how implementation changes are prepared, tested, reviewed, integrated, and promoted toward release.

The purpose is not to create a bureaucratic document collection. Each instruction should address an actual engineering concern and provide guidance that can influence implementation or verification.

Recording Architectural Decisions

Another useful practice inherited from conventional software engineering is maintaining Architectural Decision Records, or ADRs.

An ADR documents a significant engineering decision, the context in which it was made, the alternatives considered, and the rationale for selecting an approach. These records preserve information that might otherwise be lost when developers leave a project or the original design discussion is forgotten.

For our example, we might create an ADR establishing that business rules must remain independent of ASP.NET Core controllers and SQL Server persistence implementations.

That decision would explain why the separation exists, how dependencies should be structured, and what consequences it creates for future development.

An agent implementing task-assignment logic would then have access to the original architectural reasoning rather than merely encountering a rule that forbids placing business logic in a controller.

This is particularly valuable when different agents or models work on the same repository over time. The architectural decision remains available even when the agent’s conversational context does not.

Organization Standards and Project Specialization

The same approach can be extended beyond a single repository.

An organization developing multiple applications may establish shared engineering requirements covering testing, architecture, security, source control, and delivery procedures. Individual projects can then add the domain-specific requirements and architectural decisions needed for their particular applications.

The relationship resembles the way enterprise engineering standards have traditionally governed multiple development teams.

Organization-level instructions establish common principles, while project-level instructions establish how those principles apply to a particular system. Individual assignments then define the specific behavior to be implemented.

For this to work reliably, the relationship must be explicit. An agent should know which instructions are authoritative, how project-specific requirements relate to organizational standards, and what to do if it encounters conflicting instructions.

We should not assume that an AI model will infer the correct hierarchy merely because instructions exist in different directories.

The repository should make the hierarchy discoverable, and the execution process should establish how instructions are loaded and enforced.

What Goes Into the Global Instructions?

For our example, I would begin with a relatively small set of mandatory engineering instructions and expand them only as actual requirements justify doing so.

The agent should understand that the project uses a layered architecture and that business behavior belongs in the appropriate application or domain components. It should know that shared functionality is preferred over unnecessary duplication, but that new abstractions require a demonstrated need.

It should understand the testing requirements, including when to establish a failing test before implementing behavior, how to execute the applicable test suites, and why it must report failures rather than treating them as optional.

It should also understand that source code changes must remain within the scope of the assigned work, that architectural changes require explicit review, and that completion is determined by objective evidence rather than the agent’s assertion that an implementation is correct.

A short extract from our project instructions might read:

# Task Manager Engineering Instructions

## Architecture
- Preserve the established application layers.
- Keep domain rules independent of presentation
  and persistence implementations.
- Reuse existing services and interfaces where
  appropriate.
- Do not introduce architectural changes without
  documenting and obtaining approval.

## Development
- Implement only the authorized scope.
- Follow test-first development for testable
  application behavior.
- Prefer small, cohesive components.
- Refactor existing implementations rather than
  adding unnecessary workarounds.

## Verification
- Execute applicable automated tests.
- Run relevant regression tests.
- Report failures and unresolved assumptions.
- Do not claim completion without verification.

## Integration
- Follow the established Git workflow.
- Do not modify deployment configuration unless
  specifically authorized.
- Do not deploy directly to production.

This is not intended to be a complete instruction file. It demonstrates how established engineering principles can be expressed in terms directly relevant to an agent’s work.

The language is deliberately independent of the inference engine. Nothing in these instructions requires a particular model, commercial service, or agent implementation.

The project documentation defines the engineering expectations. The agent’s tools determine how those expectations are applied during execution.

Instructions Are Not the Same as Enforcement

There is an important limitation to this approach that we should acknowledge.

An AI agent is not a deterministic compiler or rules engine. Supplying an instruction does not guarantee that the agent will interpret it correctly or comply with it in every situation.

We therefore need to distinguish between engineering instructions and the mechanisms that verify or enforce them.

The instruction framework tells the agent to preserve architectural boundaries. Dependency tests and code review help determine whether those boundaries were actually preserved.

The instructions require automated testing. The CI/CD pipeline independently executes the tests against the submitted changes.

The instructions prohibit unauthorized production deployment. Environment permissions and release controls prevent the development agent from performing that operation.

These are complementary controls rather than competing approaches.

In fact, this arrangement brings us back to the engineering practices we established in the first article. Source control, environment separation, automated tests, and CI/CD are not merely supporting tools. They provide the mechanisms through which project instructions can become operational requirements.

This also suggests that agent instructions should be written with verification in mind. A statement such as “produce high-quality software” may express a useful aspiration, but it gives the agent little actionable guidance and provides no straightforward means of evaluating compliance.

A requirement to keep business logic independent of the presentation framework is more useful because it identifies an architectural boundary. A requirement to provide passing unit tests for defined business behavior is more useful because the resulting evidence can be examined.

The closer we can connect engineering instructions to observable behavior and verification, the more useful they become.

Preparing the Agent to Work

We now have the essential pieces of an AI-assisted software engineering environment.

The first article established the development infrastructure, including the repository, development tools, testing framework, and CI/CD pipeline. This article has established the architectural approach and introduced a persistent project instruction framework that captures the engineering standards we want agents to follow.

Our Task Management application has a defined architectural structure, even though its business functionality has not yet been implemented. We know where presentation logic belongs, where business rules should be maintained, and how persistence responsibilities will be isolated.

We have also established how an agent will discover the project’s engineering requirements and how its work will be evaluated against those requirements.

What we haven’t done is assign the agent any actual development work.

That distinction is intentional. The project instructions tell the agent how software should be engineered, but they do not describe the specific functionality to implement during a particular assignment.

A general instruction to follow TDD doesn’t identify which behavior should be tested. An architectural diagram doesn’t explain whether users may reassign completed tasks. A CI/CD pipeline doesn’t determine the business rules for task ownership.

Those questions require requirements analysis and functional decomposition.

Historically, business analysts, architects, and software engineers have often shared those responsibilities across separate roles. Business analysts identify what a system must accomplish, architects determine how the system should be structured, and developers implement the required functionality.

As coding agents assume more implementation work, the relationship between these activities becomes increasingly important. Someone still needs to translate business requirements into precise, independently verifiable engineering assignments.

That is where the Work Order enters the process.

The Methodology Hasn’t Disappeared

Software engineering methodologies have evolved for more than half a century, but the underlying problems have remained remarkably consistent. We need to understand requirements, manage complexity, define architecture, control changes, verify behavior, and deliver software reliably.

Each generation of methodologies has contributed something to that body of knowledge. Some practices have endured almost unchanged, while others have been adapted as development tools, computing platforms, and organizational structures evolved.

AI coding agents represent another significant change in those tools. Their ability to implement software rapidly creates new opportunities, but it doesn’t eliminate the need for the engineering practices that made complex software development possible in the first place.

What is different today is that we can express many of those practices directly as instructions for the agents performing implementation. Architectural standards, testing requirements, development procedures, and integration rules can become persistent, version-controlled parts of the project rather than relying entirely on the accumulated experience of individual developers.

That doesn’t guarantee that agents will always make the right decisions. It does, however, establish a consistent framework within which their work can be directed, evaluated, and corrected.

We began this series by establishing the development environment before writing code. We’ve now established the architecture and the rules governing implementation. The next step is to translate business requirements into bounded assignments that an agent can execute within that framework.

The tools may be new, but the progression should be familiar to anyone who has spent time engineering enterprise software.

We establish the environment, define the architecture, and then perform the work. Artificial intelligence changes who performs some of those activities, but it does not change our responsibility for the system we produce.


Next in the series: The Work Order Is the New Unit of Software Development — how software engineers combine business analysis and architectural judgment to decompose requirements, direct coding agents, and verify completed work.


Software Engineering in the Age of AI: Before the First Prompt · How We Got Here · The Work Order Is the New Unit of Software Development