We Write Code Faster with AI, but Are We Building Better Software?

How unnecessary tests, unrequested backward compatibility and repetitive documentation can turn faster AI-assisted development into growing technical debt.

Geometric blocks shifting from an orderly structure into an unstable pile, representing coding speed and technical debt

Working with AI coding tools has made me ask the same question more often: we are producing code faster, but is the quality of our software improving at the same pace?

Adding a feature, writing an integration or changing existing code has become much easier. Yet that speed can hide problems: unnecessary tests, compatibility layers nobody needs anymore and documentation that repeats the implementation.

The examples I encounter in my own work have something in common. AI tries to complete the task we give it, but its choices do not always serve the system as a whole.

More tests do not always mean more confidence

Getting developers to write tests used to be difficult. Now I sometimes need to explain to AI that it does not need to write quite so many. :)

Almost every implementation detail can receive its own test. The number grows quickly, but it is not always clear how many of those tests would catch a meaningful defect.

Tests that repeatedly check the same business rule across different layers can add maintenance work without much extra confidence. Tests tied too closely to internal code structure can also break during a simple refactoring, even when the behaviour stays the same.

We end up consuming more tokens, extending build times and producing more code that someone will have to maintain.

The question I find useful is: which important behaviour does this test protect?

Tests covering critical business rules, failure scenarios and integration boundaries are valuable. Tests that merely restate the implementation offer much less.

When a business rule changes, its tests may need to change

Imagine a field that used to be optional becoming mandatory.

The new requirement is clear: reject the operation when the field is missing. But an existing test still expects the operation to succeed without it.

Instead of questioning that expectation, AI sometimes looks for a way to keep every test passing. It might introduce a default value or preserve the old behaviour under certain conditions. The tests turn green, while the new business rule is only partly enforced.

In this case, the failure is expected because the test describes behaviour we no longer want. Tests do not replace requirements; when requirements change, the relevant test expectations need review too.

That does not mean changing every failing test. It means first establishing whether the problem is in the code or in an expectation that is no longer valid.

Backward compatibility should be a deliberate decision

I see a similar pattern with configuration changes.

We rename a field and update every place that uses it. Even so, AI may add support for both the old and new names as a precaution.

It looks harmless at first. Then the questions arrive: which field wins if both are supplied? What happens if their values differ? When will support for the old name be removed?

A simple rename has introduced extra behaviour and a new maintenance responsibility.

Backward compatibility can be necessary when customers still use an older version, services are released independently or a migration is in progress. In those cases, its scope and removal conditions should be defined. When no user or system needs the old behaviour, preserving it creates unnecessary code paths.

Documentation can become a copy of the code

AI writes documentation quickly, but it does not always distinguish what is worth documenting.

If the code contains if value == 5, it may produce a long explanation of how that condition works. A detail that is easy to read in the code now exists in a second place.

The explanation we actually need may still be missing: why 5? Which requirement sets that limit? What would changing it affect?

I want documentation to explain business logic, system boundaries, assumptions and the reasons behind architectural decisions. Repeating details that are directly visible in the code also creates more opportunities for the documentation to become outdated.

The code itself is a readable source for AI. Documentation should provide the context that makes it understandable.

Accumulated clutter affects the next task too

Individually, these additions seem small: a few extra tests, a compatibility check, several pages of explanation. Repeated with every change, they grow the codebase, lengthen builds and increase maintenance costs.

They can also make later AI-assisted work harder. Among obsolete behaviours and repetitive explanations, identifying the current requirement becomes more difficult. With a limited context window, unnecessary content also takes space that could hold relevant information.

A focused codebase and current documentation therefore matter to the quality of AI-assisted development itself.

We still own the technical decisions

Checking whether AI-generated code runs is not enough. We also need to assess whether the change matches the requirement, how much complexity it adds and what it will cost to maintain.

When reviewing a change, I find these questions useful:

  • Do the tests protect important behaviour or repeat the implementation?
  • Are there old expectations that contradict the new requirement?
  • Is the compatibility support actually needed?
  • What does the documentation explain that the code cannot?

Our instructions to AI should cover more than what to add. They should also state which behaviour is changing, which behaviour is being removed and which boundaries must remain.

I described the same division of responsibility in my article about how I use AI: we can delegate operations while retaining ownership of decisions.

In a way, we are leading a developer who works very quickly. We benefit from that speed while continuing to review decisions about design, scope and quality.

Writing code quickly does not mean making sound technical decisions.

Let AI write the code while we guide the design and the process. Our technical debt should not grow as quickly as our code output.

  • ai
  • software-architecture
  • technical-debt
  • testing
  • developer-experience