AI in the SDLC: from autocomplete to engineering partner
I’ve been using AI as part of my day-to-day development work for roughly the last 18 months, and in that relatively short period the way I use it has changed quite dramatically.
At the beginning, it was mostly what I think a lot of developers were doing.
- “Write me this method.”
- “Why is this LINQ query doing something weird?”
- “Explain this exception before I throw my laptop out of the window.”
Useful, certainly. But mostly reactive.
AI sat alongside the development process rather than really being part of it.
Fast-forward a bit, and that looks very different.
I now use AI across a much wider part of the software development lifecycle: planning changes, understanding unfamiliar code, reviewing pull requests, generating tests, investigating bugs, documenting systems, rapidly prototyping ideas and, increasingly, interacting with the wider development environment itself.
That change has made me rethink what “AI-assisted development” actually means.
Because I don’t think the interesting bit is AI writing code anymore.
The interesting bit is everything around it.
It started with autocomplete
The first genuinely useful AI development tools felt like extremely good autocomplete.
You would start writing something and the model would finish it. Sometimes brilliantly. Sometimes confidently inventing a method that had never existed in the history of .NET.
Still, it was useful.
Then the models became better at understanding larger amounts of context. Instead of completing a line, they could understand a class. Then a collection of files. Then increasingly significant portions of a repository.
And that changes the interaction completely.
Instead of:
Write a method that processes this message.
You can now say something closer to:
Investigate how messages are currently processed in this application, work out where this new behaviour belongs, identify the affected tests and propose an implementation plan.
That’s a very different capability.
It moves AI from being a typing accelerator towards being something closer to an engineering collaborator.
That distinction matters.
AI-generated code is still your code
One of the biggest mistakes I see with AI-assisted development is treating generated code differently from code written by a human.
I don’t think you should.
If anything, you should probably be slightly more suspicious of it.
AI-generated code should go through exactly the same scrutiny as everything else:
- Does it actually solve the requirement?
- Does it fit the existing architecture?
- Is there already an abstraction for this?
- What happens when things fail?
- Is it secure?
- Is it testable?
- Is it unnecessarily complicated?
- Will someone understand it six months from now?
The dangerous part of AI-generated code isn’t normally that it looks obviously bad.
It’s that it often looks very convincing.
You can get a beautifully structured implementation, complete with excellent variable names, sensible comments and a subtle race condition hiding somewhere underneath it.
The model doesn’t own the code afterwards.
You do.
Planning is often more valuable than coding
One of the biggest changes in how I use AI has been using it before implementation rather than immediately asking it to write code.
For anything reasonably complicated, I increasingly start with investigation and planning.
I’ll ask it to inspect the relevant area of the system, understand the existing patterns, identify where a change should live and highlight anything that might be affected.
Then I’ll review that plan.
Quite often I’ll challenge it.
- Why are you introducing another service here?
- Why are you modifying this boundary?
- Why do we need another abstraction?
- What breaks if this operation is retried?
- What happens if two workers process this at the same time?
That back-and-forth is surprisingly valuable.
Not because the AI is always right, but because explaining why an approach is wrong forces me to think properly about the design as well.
Used this way, AI becomes less of a code generator and more of a very patient engineer who is always available for a design discussion and doesn’t seem to mind me disagreeing with it repeatedly.
Rapid prototyping changes how you explore architecture
This is probably one of the most useful things AI has changed for me.
Before AI, testing a different architectural approach often had a fairly high cost.
You might sketch it on a whiteboard, talk it through with another developer, perhaps build a small proof of concept, and then decide whether it was worth pursuing.
That still works, obviously.
But the cost of actually building and testing competing ideas has dropped dramatically.
If I’m considering whether a feature should use direct synchronous calls, a message queue, an event-driven approach or some combination of them, I can prototype each approach far more quickly than I could before.
I can ask AI to scaffold a small version of each architecture.
Then I can run them.
Break them.
Add retries.
Simulate failures.
Look at the complexity.
Look at what becomes easier and what becomes harder.
That is much more useful than arguing about architecture in the abstract.
You can do the same thing with technologies.
- Should this service use Azure Service Bus or RabbitMQ?
- Would this work better as a background worker or through scheduled jobs?
- Do we really need microservices here, or would a modular monolith be cleaner?
- What does the authentication flow look like if we use Entra ID instead?
- How much additional infrastructure does this design introduce?
Historically, exploring several of those options properly could easily turn into days of work.
Now it is possible to get surprisingly far in a few hours.
That doesn’t mean the prototype gives you the answer automatically.
A quickly generated proof of concept is still a proof of concept.
It probably ignores operational concerns, security, deployment complexity, observability and several other things that production will eventually force you to think about.
But it gives you evidence.
And I think that is incredibly valuable.
Instead of saying:
I think architecture A is probably cleaner.
you can say:
I built both. Architecture A needed half the infrastructure, had simpler failure handling and still satisfied the requirements.
That is a much stronger engineering conversation.
It also changes how willing I am to experiment.
When the cost of trying an idea is lower, you can afford to try more ideas.
Some of them will be terrible.
That’s fine.
Finding out that an architecture is terrible after an hour is considerably better than finding out after three months.
Context changes everything
There is an enormous difference between giving a model one file and giving it an understanding of the project.
Without context, AI tends to generate what I’d describe as theoretically reasonable software.
It might be perfectly valid C#, but completely wrong for the application it is being added to.
Maybe the project uses MediatR.
Maybe it deliberately doesn’t.
Maybe repositories are used everywhere.
Maybe repositories were banned three architecture meetings ago and nobody wants to talk about it anymore.
The more context the model has about your actual engineering environment, the more useful it becomes.
That context might include:
- coding conventions
- architectural decisions
- preferred libraries
- testing approaches
- naming conventions
- domain terminology
- deployment constraints
- security requirements
- examples of good existing implementations
This is where I’ve become particularly interested in reusable AI instructions and skills.
Using skills to make AI behave consistently
One of the most useful changes I’ve made has been moving away from repeatedly explaining the same engineering expectations in prompts.
Instead, those expectations can be captured as reusable instructions.
For example:
- use the existing result pattern
- prefer async APIs
- do not introduce a new dependency without justification
- create unit tests for new domain behaviour
- use the existing logging conventions
- never log sensitive customer information
- keep controllers thin
- follow the project naming rules
None of those rules are especially exciting on their own.
But together they start encoding how a team actually builds software.
That matters because consistency is one of the harder problems when multiple developers, or multiple AI agents, are working in the same codebase.
If every AI interaction begins from a completely blank slate, you’ll get slightly different interpretations each time.
Give it a well-defined engineering environment and the output becomes much more predictable.
In some ways, it’s not particularly different from the reason we use linters, analyzers, architecture tests and coding standards.
We’ve spent years trying to remove unnecessary ambiguity from software development.
AI instructions are just another layer of that.
MCP is where things get particularly interesting
A model understanding your repository is useful.
A model understanding your development environment is much more interesting.
This is where the Model Context Protocol, or MCP, starts to become valuable.
Instead of the AI only seeing whatever code happens to be in front of it, you can give it controlled access to other systems.
That could include:
- Azure DevOps
- GitHub
- internal documentation
- ticketing systems
- databases
- monitoring platforms
- APIs
- deployment environments
Imagine investigating a bug.
Traditionally you might open the issue, read the description, search the repository, inspect logs, look through recent commits and check deployment history.
All perfectly normal development work.
But those systems are usually completely disconnected from each other from the AI’s point of view.
Give an agent controlled access to them and suddenly it can perform much more useful investigation.
- It can read the ticket.
- Inspect the relevant source.
- Look at recent changes.
- Query logs.
- Identify a likely cause.
- And then produce a proposed fix.
That doesn’t mean I want an autonomous agent happily modifying production because it spotted an exception at 3am.
I enjoy automation, but I also enjoy remaining employed.
The interesting part is connecting these systems while keeping sensible human approval points in place.
Human-in-the-loop matters
The more capable AI becomes, the more important I think human approval becomes as well.
For anything significant, I prefer workflows that look roughly like this:
- The AI investigates the problem.
- It proposes a plan.
- A developer reviews that plan.
- The AI implements the approved change.
- Tests are run.
- The developer reviews the diff.
- A pull request is created.
That still gives you the productivity benefits of AI, but keeps engineering judgement in the process.
I’m increasingly convinced this is where a lot of professional AI-assisted development will end up.
Not completely autonomous software development.
Not developers manually writing every line either.
Something in between.
Across the rest of the lifecycle
Code review is one of AI’s best uses
Another area I’ve found particularly useful is code review.
Developers are good at reviewing code, but humans have predictable weaknesses.
We get tired.
We skim repetitive changes.
We become familiar with our own assumptions.
And after spending three days implementing something, we sometimes see what we meant to write rather than what we actually wrote.
AI doesn’t remove the need for human review, but it can provide an excellent additional pass.
I often find it useful to ask questions such as:
- Are there any obvious failure scenarios here?
- Are there concurrency issues?
- Is validation missing anywhere?
- Could this expose sensitive information?
- Are there edge cases that aren’t tested?
- Does this change violate any existing architectural patterns?
- Is any of this unnecessarily complicated?
Importantly, I don’t automatically accept the answers.
AI code review can produce nonsense too.
But if it raises ten concerns and nine are useless while one catches a genuine production issue, that was probably still worth doing.
Testing is another obvious fit
AI is very good at generating tests.
It is slightly less good at knowing which tests actually matter.
That distinction is important.
Asking:
Write unit tests for this method.
will usually result in something.
But asking:
Identify the important behaviours, boundary conditions, failure scenarios and regression risks for this change.
is much more useful.
Once you’ve agreed what needs testing, generating the repetitive test code becomes the easy part.
This is one of the broader patterns I’ve noticed with AI development.
The quality of the result improves dramatically when you separate thinking from implementation.
First decide what needs doing.
Then let the model help do it.
Debugging with AI
Debugging is another area where AI has become part of my normal workflow.
If you give a model:
- the exception
- the stack trace
- the relevant source
- recent changes
- logs
- a description of what should have happened
it can often produce a very good set of hypotheses.
That keeps the investigation grounded.
Otherwise there’s a temptation for the model to immediately start modifying code before anyone has actually established what the problem is.
Which, to be fair, is also something developers occasionally do.
Documentation becomes much cheaper
Documentation is one of those things almost every engineering team agrees is important right up until somebody actually has to write it.
AI dramatically reduces the cost.
READMEs, architecture decision records, release notes, API documentation, onboarding guides and implementation summaries can all be generated from the actual work.
The important distinction is that AI shouldn’t invent the documentation. It should help describe what really exists.
I particularly like generating documentation after implementing something while the context is still available.
If I’ve just built a messaging workflow, for example, the model already understands the queue, consumers, retry behaviour, dead-letter handling and configuration.
Getting it to create a first draft of the documentation at that point takes minutes.
Future me generally appreciates this.
Future me has inherited enough undocumented systems from past me already.
AI is very good at refactoring, until it isn’t
Refactoring is another area where AI can be incredibly useful.
Renaming things across a codebase, extracting interfaces, replacing repetitive patterns, modernising older syntax or migrating APIs are all good candidates.
But large refactors are also where I become cautious.
Software often contains behaviour that isn’t obvious from reading a single piece of code.
There are historical decisions.
Strange integrations.
Odd edge cases.
That terrifying comment from 2019 saying:
// Do not simplify this. Production breaks.AI doesn’t automatically know any of that.
This is another reason why repository context, tests and small commits matter so much.
AI makes large changes easy.
Git makes those large changes reversible.
I consider that a healthy relationship.
Security and privacy can’t be an afterthought
As AI becomes more deeply integrated into development workflows, organisations also need to think seriously about what information models are allowed to access.
Source code can be commercially sensitive.
Logs can contain customer information.
Configuration files can contain secrets.
Internal documentation can contain things you absolutely do not want leaving the organisation.
There needs to be a clear understanding of:
- which models are approved
- how data is processed
- whether prompts are retained
- what information agents can access
- what actions agents are allowed to perform
- where human approval is required
This is much less exciting than watching an agent build a feature by itself.
It is also probably much more important.
AI can make bad developers faster too
There is a slightly uncomfortable point in all of this.
AI improves productivity.
But productivity and quality are not the same thing.
It is now remarkably easy to produce an enormous amount of code very quickly.
That does not necessarily mean you’ve produced a good system.
In fact, AI arguably increases the importance of good engineering judgement.
If generating code becomes cheaper, the valuable skills move further towards understanding:
- what should be built
- where it should live
- how systems should interact
- what failure looks like
- how something should be tested
- how it should be operated
- when complexity is justified
The bottleneck moves away from physically typing code.
And I think that is a good thing.
Junior and senior developers probably get different benefits
I also think AI affects developers differently depending on experience.
For junior developers, it can be an incredible learning tool.
You can ask questions about unfamiliar code, get explanations, explore alternative implementations and understand concepts far faster than searching through documentation alone.
But there’s also a danger.
If every difficult problem immediately becomes:
AI, solve this for me.
you can skip the bit where the understanding develops.
For more experienced developers, I think the leverage can be enormous.
If you already understand architecture, testing, distributed systems, security and software design, AI lets you apply that knowledge much faster.
You’re not relying on the model to know whether something is good.
You’re using the model to accelerate implementation while you provide the judgement.
That distinction is important.
Where I think this is heading
The direction seems fairly clear to me.
AI development tools are moving from:
- Autocomplete
- Chat
- Repository-aware assistants
- Agents
- Agents connected to the wider engineering environment
The interesting question is how much autonomy we actually want to give them.
I suspect the answer will vary depending on risk.
- Automatically updating documentation after a pull request? Fine.
- Generating unit tests? Probably fine.
- Opening a pull request containing a proposed fix? Potentially very useful.
- Automatically diagnosing an issue, modifying production infrastructure and deploying a fix without anyone looking at it? I’ll let somebody else volunteer first.
What I expect we’ll see instead is increasingly sophisticated workflows with clear approval points.
AI handles the repetitive investigation and implementation.
Developers provide context, judgement and accountability.
The developer still matters
After using AI heavily for the last 18 months, I’m probably more optimistic about it than I was at the beginning.
But I’m also less interested in the headline question of whether AI will “replace developers”.
I think that question misses what is actually happening.
The job is changing.
The amount of code a developer can produce is increasing dramatically.
The amount of information we can process is increasing.
The speed at which we can explore unfamiliar systems is increasing.
The number of ideas we can prototype and test before committing to a design is increasing too.
But somebody still needs to understand the problem.
Somebody still needs to make architectural decisions.
Somebody still needs to recognise when the beautifully formatted answer is completely wrong.
And somebody still needs to take responsibility when the software reaches production.
AI doesn’t remove the need to be a good developer.
If anything, I think it makes being a good developer more valuable.
Because when writing the code becomes easier, knowing what code should exist becomes the important part.
And personally, I find that far more interesting anyway.