For most of my career, building software meant writing the implementation myself.

Design the solution. Create the classes. Write the business logic. Configure the database. Implement validation. Write the tests. Fix the build. Deploy. Repeat.

I still do all of those things. But increasingly, I don't personally type all the code that performs them.

AI agents now handle a significant portion of implementation work for me.

That statement can easily sound like: “I let AI build my software.”

That's not what I mean.

The biggest change AI has made to my software development lifecycle isn't that I've stopped engineering. It's that my engineering effort has moved to a different level.

I spend more time defining intent, designing architecture, establishing constraints, creating guardrails, reviewing implementations, challenging decisions, validating behavior, thinking about production, and preserving engineering context for the people who will work on the system after me.

The AI writes more of the code. I still own the engineering outcome.


This isn't vibe coding

There's a style of AI-assisted development where you describe roughly what you want, let the model generate something, run it, and continue prompting until the application appears to work. That can be useful for prototypes and experimentation.

But it's not how I want to build production systems.

For production software, I don't want an AI agent deciding the architecture as it goes. I want the architecture to constrain the agent.

Engineering Intent
        ↓
Architecture
        ↓
Standards & Conventions
        ↓
AI Governance & Guardrails
        ↓
Specification
        ↓
AI Agent Implementation
        ↓
Human Review
        ↓
Automated Validation
        ↓
CI/CD
        ↓
Production
        ↓
Observability
        ↓
Feedback & Learning

I've started thinking of this as AI-governed development. I'm not presenting that as a formal industry methodology. It's simply the best description I've found for how my own development process is evolving.

The objective isn't to give AI unlimited freedom. It's to give AI enough freedom to move quickly inside engineering boundaries that have already been established.


A recent integration API changed how I think about this

A recent integration API I worked on made this transition particularly visible to me. It was built on .NET 10, but the interesting part wasn't the framework version or how quickly endpoints could be generated.

The system had real architectural, security, delivery, and operational requirements.

Application Architecture
├── .NET 10
├── CQRS
├── EF Core
├── Result / Error patterns
├── FluentValidation
├── Global Exception Handling
└── Cross-Cutting Concerns

Security
├── JWT Authentication
├── RS256 asymmetric signing
├── Protected secrets / configuration
├── Argon2id password hashing
└── OAuth 2.0 Client Credentials Grant

Database Evolution
├── EF Core
├── EF Core migrations
└── Flyway

Delivery
├── Jenkins
├── CI/CD pipelines
├── Automated tests
├── Code review
└── Quality gates

Observability
├── OpenTelemetry
├── Structured logging
├── Metrics
├── Distributed traces
├── Correlation context
└── Centralized investigation tooling

AI agents helped me implement significant portions of this much faster than if I had manually typed every handler, validator, configuration class, test, migration, and supporting component.

But AI didn't eliminate the need to understand why those pieces existed. If anything, the faster implementation became, the more important those engineering decisions became.


Architecture became a guardrail

Architecture isn't merely a diagram showing developers where to put classes. With AI-assisted development, architecture also becomes a constraint on what an agent is allowed to produce.

If the application uses CQRS, the agent shouldn't invent a completely different application structure for the next feature. If expected failures use an established Result/Error pattern, generated code shouldn't suddenly throw exceptions for ordinary business outcomes. If validation belongs in a common pipeline, every generated handler shouldn't invent its own validation mechanism.

This is how this application behaves.

That reduces architectural improvisation. And that's exactly what I want.


Security shows why this isn't just prompting

Authentication wasn't simply Add JWT authentication → Done.

The API had explicit requirements around JWT authentication, asymmetric RS256 signing, protected configuration and signing-key material, Argon2id password hashing, and OAuth 2.0 Client Credentials Grant for machine-to-machine integration.

Generating authentication code and designing an authentication model are different responsibilities.

Someone still needs to reason about trust boundaries, choose the appropriate authentication flow, protect secrets and signing material, understand token validation and authorization behavior, and decide how those requirements will be verified before production.

AI can help implement those decisions. It shouldn't silently make all of them for me.


Cross-cutting concerns became part of the governance

I don't want every endpoint or handler deciding independently how validation, logging, and failures should work. Instead, common policies can be established through FluentValidation, global exception handling, Result/Error patterns, cross-cutting pipelines, structured logging, and OpenTelemetry.

This isn't only about avoiding duplicated code. It's about making certain engineering decisions consistent by default.

The less a critical guardrail depends on someone remembering it, the stronger that guardrail becomes.


AI governance is more than a prompt

A long prompt saying “follow the architecture, use best practices, and generate production-quality code” isn't governance. Governance needs mechanisms.

So I started developing and refining repository rules, AI skills, specialized agent workflows, engineering conventions, automated validation, and production-readiness checks around the project.

AI Governance
│
├── Repository Rules
│   ├── Architecture conventions
│   ├── Coding standards
│   ├── Security requirements
│   └── Testing expectations
│
├── AI Skills
│   ├── Production-readiness review
│   ├── Security checks
│   ├── Code-quality checks
│   └── Test enforcement
│
├── Agent Workflows
│   ├── Implementation
│   ├── Review
│   └── Validation
│
└── Automated Guardrails
    ├── Build
    ├── Tests
    ├── Static analysis
    └── CI/CD quality gates

At first, this can look like tooling around the “real development.” I'm beginning to see it differently. It is part of the development.

An instruction tells the agent what I expect. A guardrail helps detect when the implementation violates that expectation.


Guardrails matter more when code becomes cheap

AI has made generating code remarkably inexpensive. That's powerful. But AI has also made generating bad code remarkably inexpensive.

Earlier in my career, one major constraint was: How quickly can I implement this?

Now I'm increasingly concerned with whether we're implementing the right thing, respecting the architecture and security boundaries, following established patterns, proving behavior with tests, and leaving enough evidence to diagnose production problems.

Code-generation speed doesn't reduce the need for engineering discipline. I think it increases it.


Don't silently implement something you believe is wrong

One lesson reinforced by recent professional feedback from an architect I work with is the importance of not being a passive builder.

He specifically recognized that I would challenge a design when I believed something was wrong rather than simply implementing it without discussion.

That matters even more now that AI agents can produce implementation so quickly. If I see an architectural concern, my responsibility isn't to quietly let the agent implement it because “that's what the design says.” It's to raise the concern.

Sometimes my concern will be correct. Sometimes the architect will have context that I don't have. Sometimes the discussion will reveal a better option than either of us originally considered.

The important thing is that the disagreement becomes an engineering conversation rather than a hidden assumption.

Respecting architecture means taking it seriously enough to question it when implementation reveals something the original design didn't anticipate.

A good engineer follows the architecture. A responsible engineer also speaks up when reality gives them a reason to question it.


Architecture and reality don't always stay identical

Requirements evolve. Integration behavior becomes clearer. Security requirements emerge. Infrastructure imposes limitations. Operational concerns appear.

The dangerous part isn't that implementation can evolve. The dangerous part is when it changes silently.

Original Architectural Direction
              ↓
     Implementation Finding
              ↓
      Engineering Discussion
              ↓
        Agreed Decision
              ↓
      Actual Implementation
              ↓
 Documented Reason / Deviation
              ↓
   Updated Engineering Context

If the architecture originally suggested Approach A but we deliberately implemented Approach B, I want the next engineer to understand what was intended, what we discovered, why we challenged it, what we decided, and what is now the actual state of the system.

That's not documentation for documentation's sake. It's preserving engineering intent.


Document the distance between architecture and reality

If documented architecture says one thing while the source code consistently does something else, what should the next AI agent follow?

Without context, it might “correct” an intentional implementation decision. Or it might continue spreading a deviation that was originally supposed to be temporary.

Don't just document the architecture. Document the distance between the architecture and reality.

And explain why that distance exists. That gives both humans and AI agents much better context.


The repository became more than source code

I don't want a repository to contain only the final source code. I want it to preserve enough engineering context to explain how the system reached its current state.

Engineering Context
│
├── Architecture
│   ├── Intended architecture
│   ├── Architectural boundaries
│   └── Established patterns
│
├── Implementation
│   ├── What was actually implemented
│   ├── Important implementation details
│   └── Technical decisions
│
├── Change History
│   ├── What changed
│   ├── Why it changed
│   └── Relevant context
│
├── Architecture Deviations
│   ├── Original direction
│   ├── Actual implementation
│   ├── Reason for deviation
│   └── Resulting implications
│
├── AI Governance
│   ├── Repository rules
│   ├── Skills
│   ├── Agent workflows
│   └── Guardrails
│
└── Delivery Knowledge
    ├── Documentation
    ├── Operational considerations
    └── Production readiness

The code tells you what the system does. It doesn't always tell you why the system ended up that way.


Documentation became part of delivery

I used to think about documentation mostly as something that accompanied software. I'm increasingly treating it as part of the engineering deliverable itself.

When I finish significant work, I'm not only asking whether it compiles, whether the tests pass, and whether the pipeline succeeded. I'm also asking whether another developer can understand what I did, whether the delivery team understands the current state, whether the architect can see where implementation differs from the original direction, and whether the business can understand what capability was actually delivered.

Implementation
      +
Tests
      +
Security
      +
CI/CD
      +
Observability
      +
Documentation
      +
Decision History
      +
Known Deviations
      +
Operational Context
      =
Engineering Delivery

The code is a major part of the deliverable. But the code isn't the entire deliverable.


Different people need different context

The developer who inherits the system doesn't need exactly the same information as the architect. And the business doesn't need a detailed explanation of every CQRS handler.

The next developer needs patterns, boundaries, deviations and guardrails. The delivery team needs implementation state, dependencies and operational considerations. The architect needs to understand how reality compares with architectural intent. The business needs to understand the capability, remaining risks and what it enables next.

Good engineering communication doesn't mean giving everyone the same enormous technical document. It means preserving enough context that the right information can be communicated to the right audience.


Build for the next engineer, not just the current feature

Eventually, somebody else will inherit what I build. Maybe another developer, technical lead, architect, or someone investigating a production incident months later. Increasingly, maybe another developer working with an AI agent.

I don't want that person to reverse-engineer every decision I made. I want them to inherit more than source code. I want them to inherit engineering context.

Instead of every developer starting over with AI, the project itself carries part of the engineering knowledge forward.


AI governance became a knowledge-transfer mechanism

It's easy to think of guardrails as mechanisms for stopping AI from doing something wrong. But there's another purpose: knowledge transfer.

We've always preserved knowledge through README files, architecture diagrams, coding standards, onboarding documents, and architecture decision records. AI introduces another possibility: some of that knowledge can become machine-consumable engineering context.

          ENGINEERING INTENT
                 │
                 ▼
       MACHINE-READABLE GOVERNANCE
       Rules · Skills · Agent Workflows
                 │
                 ▼
          ENFORCED GUARDRAILS
      Tests · Analysis · CI/CD · Review

Intent explains why. Governance communicates how we expect work to be performed. Guardrails help verify that important expectations weren't ignored.


If the next developer needs me for everything, I haven't finished

Documentation can't replace collaboration. AI can't replace judgment. But I don't want important architectural knowledge to disappear when I close my laptop.

I don't want an implementation decision to exist only because “Edwin remembers why we did that.”

If the next developer needs me to explain everything before they can safely change the system, then my job isn't finished.

The most valuable thing I leave behind shouldn't be the code I personally typed. It should be a system where another engineer can understand the architecture, understand the reasoning, challenge old decisions when appropriate, and continue building without depending entirely on me.


Observability is part of the architecture

Shipping the API wasn't the finish line. I also needed the system to tell us what it was doing after deployment.

The design included OpenTelemetry, structured logs, metrics, distributed traces, and correlation context, with telemetry available to centralized investigation tooling such as Kibana.

Incoming Request
       │
       ├── Correlation Context
       │
       ▼
Application / CQRS Pipeline
       │
       ├── Structured Logs
       ├── Metrics
       ├── OpenTelemetry Traces
       └── Error / Result Context
               │
               ▼
        Telemetry Platform
               │
               ▼
            Kibana
               │
               ▼
      Production Investigation

The objective wasn't simply to generate more logs. It was to make production problems investigable.

Logs help tell me what happened. Metrics help tell me how the system is behaving. Traces help show where time and failures traveled. Correlation context helps connect evidence belonging to the same operation.

Those aren't code-generation questions. They're production-engineering questions.


Production gets the final vote

A generated implementation can compile. Tests can pass. Static analysis can be clean. Code review can approve it. The pipeline can deploy it. And we still haven't proven that the system will behave perfectly under real production conditions.

That's why my SDLC doesn't end at deployment:

Deploy → Observe → Investigate → Learn → Improve

What I learn can feed back not only into the application, but also into the architecture, documentation, rules, skills, tests, and AI guardrails. The governance itself should evolve.


The engineer still needs to understand the code

AI can allow someone to generate software they don't fully understand. That may work surprisingly well—until something goes wrong.

When an EF Core query behaves badly, I still need to understand data access. When telemetry isn't enough, I still need to understand observability. When an API leaks information across a boundary, I still need to understand security. When authentication fails, I still need to understand the trust model.

AI reduces the cost of implementation. It doesn't eliminate the cost of understanding.

In some ways, reviewing large amounts of machine-generated code demands stronger fundamentals because I'm evaluating implementation that I didn't personally type.


My job has shifted from typing to directing

For years, developer productivity was closely associated with writing code. Now I can spend significant time defining a problem carefully and have an agent implement something that previously would have taken much longer to type manually.

That doesn't necessarily mean I performed less engineering. Sometimes it means I concentrated more of the engineering into the decisions that matter.

Define → Govern → Generate → Review → Validate → Deploy → Observe → Learn → Own

Define the problem. Govern the architectural, security, quality, and operational boundaries. Generate with AI agents. Review the output. Validate with tests and guardrails. Deploy through a controlled pipeline. Observe the real system. Learn from feedback. And finally, own the outcome.


“The AI wrote it” isn't a production incident response

I can't tell a production incident: “The AI wrote that part.”

If I instructed it, reviewed it, approved it, shipped it, and operate it, then it's my software.

Architecture, security, quality, observability, documentation, and production are still my responsibility.

AI changed who typed much of the implementation. It didn't change who owns the engineering outcome.


Experience still matters — just differently

Another observation from recent professional feedback described me as “open-minded and quick to pick up new technology.”

After more than two decades working in software, I think that matters more than ever. I don't want to defend the way I built software ten years ago simply because it's familiar. But I also don't want to discard decades of engineering lessons simply because an AI can now generate code.

What should change? And what engineering principles should survive the change?

Earlier in my career, experience helped me write code faster. Today, experience increasingly helps me decide what code should exist in the first place.


This is becoming part of technical leadership for me

Technical leadership used to make me think primarily about reviewing developers' code, discussing architecture, establishing standards, and helping teammates make good engineering decisions.

Those responsibilities haven't disappeared. But now there is another participant in the development process: the AI agent.

Creating repository rules, reusable skills, agent workflows, architecture guidance, automated guardrails, decision history, and documentation isn't separate from technical leadership. It's becoming another way I practice it.

Because good technical leadership isn't only about personally knowing the right answer. It's also about creating an environment where other engineers can make good decisions without constantly depending on you.


I'm building for whoever comes next

I started this transition thinking AI would mainly help me write code faster. It did.

But the more interesting change has been what happened to my role around the code.

I'm spending more of my time defining intent, designing architecture, challenging decisions, establishing governance, building guardrails, reviewing generated implementation, validating security and quality, designing observability, preserving decisions, documenting deviations, and leaving enough context for the people who come next.

I'm not just building for today's requirement.

I'm building for the next developer who will have to understand it. For the delivery team that has to support it. For the architect who needs to understand how the design evolved. For the business that needs to understand what was actually delivered. And increasingly, for the next AI agent that will eventually be asked to change it.

It has moved more of my engineering effort from typing implementation to designing the environment in which implementation happens.

If another developer can open that repository, understand the intent and decisions, see where reality differs from the original architecture, use the established guardrails, and safely continue the work without needing me beside them—then I built more than software. I left behind engineering context.


I haven't stopped writing software

I just don't write every line anymore.

After more than two decades in this industry, that's a strange sentence to write. I started in an era where computers needed far more explicit instructions from humans. Today I can describe intent to an AI agent and watch it implement a substantial part of a system.

But responsibility hasn't moved with the typing.

Architecture is still mine to understand. Security is still mine to question. Quality is still mine to protect. Production is still mine to observe. Context is still mine to preserve. The outcome is still mine to own.

AI helped me move faster because I no longer have to manually type every implementation detail. Governance lets me move faster without surrendering engineering control. Observability makes sure that control doesn't end at deployment. Documentation and decision history help make sure the engineering knowledge doesn't end with me.

AI can generate the code. Engineers still have to engineer the system.

Or, to put it another way:

Some developers became managers. I became the guy reviewing code written by robots. 🤖

And apparently, now I also have to leave documentation explaining to the next developer why the robots did what they did. 😄

Learning the Next Thing.


Some observations in this article are informed by professional feedback I received during my 2026 performance cycle. The excerpts are included as context for my engineering experience and do not represent an endorsement by my employer.