My old workflow with AI was straightforward: explain a feature, ask for the implementation, copy or adapt the code, and repeat.

It felt productive. An endpoint that might have taken me an afternoon could appear in seconds. If something failed, I pasted the error back into the conversation. There was always another answer, another version, another thing to try.

I was producing code quickly. But I started noticing that having an implementation did not always mean I understood the decisions inside it.

Why was the logic in that file? Why did the database need that relationship? Why had I introduced another layer when I could barely explain what the first one was responsible for?

I could ask AI to change the code. What I needed was a better reason for changing it.

Code stopped being the hardest question

As my projects became more involved, the difficult parts increasingly happened before I wrote the implementation.

Building an endpoint meant deciding what it should accept, who could access it, what it should return, and what should happen when something went wrong. Connecting a database meant thinking about relationships, ownership, and which rules the data needed to follow.

The questions became less about syntax:

Should this logic live here? What happens if this request runs twice? Can one user access another user's data? Am I adding complexity because the project needs it, or because the architecture looks impressive?

AI could generate an implementation for almost any direction I chose. That made choosing the direction more important.

If I asked it to build around a weak assumption, I could end up with a lot of code supporting that assumption before I had even questioned it.

I started bringing my own answer

The biggest change was making myself think through an approach before opening the conversation.

I did not need a perfect design. Sometimes it was a few sentences: what I was building, how I planned to handle it, and why I thought that approach fit.

Then I could ask more useful questions:

“Here’s my approach. What assumptions am I making?”

“Challenge this database design. Which rules does it fail to enforce?”

“What edge cases am I missing?”

“Give me reasons not to use this architecture.”

I also started including constraints. A student project with a small team and limited time needs different decisions from a service handling heavy traffic. Asking about 10,000 users can be a useful thought experiment, but it does not mean I need to build for them today.

A useful review should help me identify what matters now and what can wait.

What this looks like in practice

Consider a backend that saves a report and sends an SMS notification.

My first approach might be to handle both inside the same request: validate the report, save it, call the SMS provider, and return a success response.

Before asking for code, I can describe that flow and ask AI to challenge it.

What happens if the report is saved but the SMS fails? Does the user see an error even though the report exists? If they retry, could they create a duplicate? If the SMS provider is slow, how long should the request wait?

Those questions expose decisions that a working demonstration might never reveal.

AI might recommend a background job. That gives me another decision to evaluate: does this project justify the additional setup and failure handling? What guarantee do I actually need for notifications? How will I know when delivery fails?

The conversation becomes useful because I have something specific to examine. I can revise my approach, keep it with a clearer understanding of its limits, or investigate a question I cannot answer yet.

I make the decision. AI puts pressure on the reasoning behind it.

A confident review can still be wrong

This workflow has its own trap. It is easy to stop blindly accepting generated code and start blindly accepting generated criticism.

An explanation can sound convincing because it uses familiar engineering language. A recommendation to add caching, queues, or another abstraction can feel authoritative even when the project has no clear need for it.

I try to ask what specific failure a recommendation prevents and whether that failure is relevant to my situation. If AI claims something about a framework or library, I can check the documentation. If it predicts a bug, I can trace the behavior or write a focused test.

Sometimes its criticism reveals a real gap. Sometimes it relies on an assumption that does not apply.

I need to be able to disagree with the answer and explain why. Otherwise, I have only moved the same dependency to an earlier stage of development.

I started learning more from the conversation

As a Computer Science student, this is the part I value most.

When I ask for an implementation, I can finish with code I recognize but cannot fully explain. When I bring an approach for review, I have to put my understanding into words. That often reveals the gaps before AI even responds.

If I cannot explain why I separated two components, maybe I copied a pattern without understanding its purpose. If I cannot describe what happens after a failure, maybe I have only designed the successful path.

Review gives me specific things to study. I can connect a concept to a decision in my own project, then use what I learn to improve it.

I still ask AI to help write code. The title describes a change in where I place the most value. These days, I bring it API contracts, database schemas, architecture choices, failure scenarios, security questions, test ideas, and code I have already written.

I am still learning how to make those decisions well. Having a tool question them helps, provided I keep checking its reasoning too.

The habit I want to keep is simple:

Don’t just ask AI, “How do I build this?”

Build your own answer first. Then ask AI, “What’s wrong with my thinking?”