AI Can Help You Code Faster. It Cannot Make You a Better Engineer.

Practical lessons from using AI in real software projects, including project instructions, context management, code review, developer ownership, and why judgement still matters.

An abstract system of structured modules expanding through an AI amplification network An abstract system of structured modules expanding through an AI amplification network

AI has changed the way I write software.

I use it constantly now. I use it to understand unfamiliar code, explore architectural options, generate repetitive pieces, debug issues, write tests, review implementations, and sometimes just think through a problem before touching the code.

It has absolutely made me faster.

But after using AI extensively myself and seeing different developers use it across actual projects, one thing has become very clear:

AI multiplies the way you already work.

A disciplined developer can use AI to move ridiculously fast without destroying the codebase. An undisciplined developer can generate an impressive amount of bad code in a very short time.

Most of the problems I see are not really AI problems. They are engineering discipline problems that AI has made easier to expose.

These are some of the most important things I have learned while using AI in development and while leading teams that use it.

Give the AI Proper Instructions

I do not like depending on the default behaviour of whichever coding tool or model I happen to be using.

Every serious project should have a shared rules file that tells the AI how the project works and what it is expected to follow.

Without that, the AI will happily introduce a new pattern into every second file, install another package for something the project already supports, duplicate components, create unnecessarily large modules, or use whatever conventions were most common in its training data instead of the conventions used in your repository.

The rules do not need to be philosophical. They need to be practical.

Some of the instructions I generally care about are:

  • Suggest a properly structured commit message after completing a task.
  • Follow the existing folder structure and architectural patterns.
  • Check for reusable components and utilities before creating new ones.
  • Do not silently introduce new libraries when the same thing can be done with existing dependencies.
  • Use exact dependency versions rather than loose version ranges.
  • Avoid packages that were released only a few hours or days ago unless there is a strong reason to use them.
  • Keep files within a reasonable size and split code when responsibilities start getting mixed.
  • Do not perform unrelated refactoring while completing a focused task.
  • Run the relevant type checks, tests, linting, and build commands before considering the work complete.
  • Explain any assumption made because of an unclear requirement.

These instructions will not make the AI perfectly consistent. Nothing will.

But in my experience, a good rules file gets you surprisingly close. I would say roughly 80 to 90 percent of the output starts moving in the right direction.

That remaining 10 to 20 percent is exactly why a human still needs to understand and review the work.

Keep Updating the Rules

A rules file should never be treated as something you write once and forget.

Whenever I notice the AI repeatedly doing something stupid, I add a rule for it.

If it creates duplicate components, that becomes a rule.

If it starts changing unrelated files, that becomes a rule.

If it adds another dependency for a ten-line utility, that becomes a rule.

If it ignores an existing abstraction and builds a parallel system, that definitely becomes a rule.

Over time, the instruction file becomes a record of the mistakes the team and the AI have already made. Ideally, those mistakes do not need to be repeated.

The same applies at the team level.

When one developer finds a prompt or instruction that consistently produces better results, that learning should not remain hidden inside their personal workflow. The useful parts should become shared project knowledge.

How Developers Use AI Matters More Than Which AI They Use

Teams often spend too much time discussing models and not enough time discussing how developers are actually using them.

A better model cannot completely rescue a poor workflow.

I have seen developers give the AI a vague one-line instruction, accept a large patch without properly reading it, and then blame the tool when the implementation breaks.

I have also seen developers use the same tools very effectively by giving the model the correct files, explaining the actual requirement, defining the boundaries, reviewing every meaningful decision, and refining the result in small steps.

The difference is rarely just the model.

It is the interaction.

That is why I believe engineering leads should occasionally sit with developers and observe how they work with AI.

Not as surveillance, and not to judge how many prompts they use.

The goal is to understand:

  • How they explain a task.
  • Whether they provide the right context.
  • Whether they verify the AI’s assumptions.
  • How they handle a wrong response.
  • Whether they review the generated code or merely test whether it runs.
  • Whether they keep one conversation alive forever and overload it with unrelated context.
  • Whether they understand what was generated well enough to maintain it later.

A short pair-programming session often reveals more than looking at a final pull request.

Sometimes the developer does not have a coding problem at all. They have a prompting, context management, or review problem.

Do Not Turn Every Chat into an Archaeological Site

Long AI conversations feel convenient because the tool appears to remember everything.

In reality, a huge conversation often becomes noisy.

Old requirements, outdated decisions, previous experiments, and unrelated files remain in the context and begin influencing the next answer.

I have made this mistake myself.

You continue in the same thread because it already knows the project, but after a while the responses become slower, less precise, and occasionally confused by something discussed much earlier.

For a new and reasonably isolated task, I now prefer starting a fresh conversation and giving it only what it needs:

  • The relevant project instructions.
  • The exact requirement.
  • The specific files or modules involved.
  • Any constraints that must not be violated.
  • The commands used to validate the result.

More context is not automatically better context.

The goal is not to show the AI the entire repository and hope it figures things out. The goal is to provide the smallest amount of context required to make a good decision.

This also matters when the team is using limited API quotas or expensive models. Careless context usage burns tokens without necessarily improving the work.

Keep Enough Evidence to Explain Important Decisions

I do not think developers need to archive every single prompt forever.

That would create noise and turn normal development into paperwork.

But for important architectural decisions, difficult debugging sessions, production incidents, unusual implementation choices, or places where AI influenced the direction significantly, it helps to retain enough context to explain what happened.

That may include:

  • The relevant prompt or conversation.
  • Terminal output.
  • Error logs.
  • Benchmark results.
  • A short written explanation in the task or pull request.
  • Links to documentation that influenced the decision.

The purpose is not to defend yourself after something goes wrong.

The purpose is to make the reasoning understandable to the next person, including your future self.

A decision that only exists inside an expired AI conversation is not useful project knowledge.

AI Tool Limitations Must Be Treated as Real Constraints

AI tools have limits.

They can hit quotas.

They can slow down.

A model may suddenly produce worse output for a particular task.

A coding assistant may fail to understand a large repository.

An API may become temporarily unavailable.

A developer may also discover that a task which looked easy with AI still needs to be completed manually.

These things should not be hidden.

If estimates depend heavily on AI-assisted speed, then the team should understand that dependency.

When an AI tool becomes a bottleneck, the developer should communicate:

  • What limitation was encountered.
  • Whether the task can continue manually.
  • How much the manual approach affects the estimate.
  • Whether another tool or model can be used.
  • Whether the original timeline still makes sense.

AI is part of the workflow now, but it is still a tool.

A tool limitation may explain why an estimate changes. It does not remove the responsibility to communicate the change.

You Own Every Line the AI Generates

This is probably the most important rule:

You cannot blame the AI for code that you reviewed, committed, and deployed.

AI did not merge the pull request.

AI did not approve the architecture.

AI did not decide that the tests were good enough.

A developer did.

I have no problem with someone saying, “The AI generated the first version of this.” That is simply describing the workflow.

But “the AI wrote it” is not a valid explanation for a bug, security issue, bad abstraction, or broken production deployment.

Once you accept the code, you own it.

That means you should be able to explain:

  • What the code does.
  • Why this approach was chosen.
  • What assumptions it makes.
  • What can fail.
  • How it was tested.
  • How another developer can safely modify it.

This is also why I dislike massive AI-generated changes that developers cannot explain during review.

Even when the code looks clean, it creates a dangerous illusion of progress.

Code that nobody properly understands is not finished work.

It is delayed debugging.

AI Makes Finished-Looking Code Too Easy to Produce

One of the most deceptive things about AI-generated code is how complete it can look.

The naming is clean.

The files are organised.

The comments sound confident.

The code may even compile on the first attempt.

Then you discover that it misunderstood a business rule, skipped an authorization check, introduced a race condition, duplicated existing functionality, or handled only the exact example given in the prompt.

This is where developers need to be especially careful.

The better AI becomes at producing convincing code, the more important it becomes to review the logic rather than just the syntax.

A clean implementation can still be the wrong implementation.

A passing build can still hide a bad architectural decision.

A successful demo can still fall apart when the real edge cases arrive.

The Bottleneck Is Moving Away from Typing

There is a strange assumption that because AI can generate code quickly, software development will require less discipline.

I have experienced the opposite.

When code becomes easier to produce, it becomes easier to produce too much of it.

It becomes easier to create abstractions nobody asked for, add dependencies nobody evaluated, duplicate existing logic, expand the scope without noticing, and generate implementations that look convincing enough to escape a shallow review.

The bottleneck is slowly moving away from typing code.

The harder parts are becoming:

  • Understanding the problem correctly.
  • Choosing the right boundaries.
  • Giving the AI useful context.
  • Rejecting plausible but incorrect solutions.
  • Reviewing architecture rather than only syntax.
  • Knowing what not to build.
  • Taking responsibility for the final result.

AI is an extremely powerful engineering tool.

I would not want to return to working without it.

But it does not replace judgement, ownership, or engineering ability.

It simply makes the presence, or absence, of those qualities much more visible.