AI coding agents and the code smells they leave behind

Somewhere along the way, the standard advice in data science became “just prompt it and ship it.” AI coding agents are fast enough to make that feel productive, and the long-term health of your codebase is what pays for it.

I have spent more than 14 years cleaning up WordPress and WooCommerce sites that were “built by a guy who knew a guy.” These days that guy is an LLM. AI coding agents produce syntactically perfect code in seconds, but they have no intuition for architecture, so if you cannot steer them you only reach the technical debt faster.

From writing code to reviewing it

The typing part of the job has been offloaded, so most of what is left is review. You are now the senior developer looking over the shoulder of a very fast, very obedient junior that is often wrong about structure. The skill worth having is architectural judgment.

Two code smells show up again and again in what AI coding agents hand back, and both turn a short script into something nobody wants to touch: divergent change and speculative generality.

1. Divergent change: the do-it-all class

Divergent change is one class or module doing too many unrelated things, which is the Single Responsibility Principle being violated. Ask an agent to “add functionality to handle X” and it will almost always stuff that code into whatever class you already have open.

This kind of ML pipeline class turns up constantly:

class ModelPipeline:
    def __init__(self, data_path):
        self.data_path = data_path

    def load_from_s3(self):
        print(f"Connecting to S3 to get {self.data_path}")
        return "raw_data"

    def clean_txn_data(self, data):
        print("Cleaning specific transaction JSON format")
        return "cleaned_data"

    def train_xgboost(self, data):
        print("Running XGBoost trainer")
        return "model"

That one class handles infrastructure with S3, data engineering with the cleaning step, and ML research with the trainer, which gives it three separate reasons to break. Change the bucket permissions and you edit this file. Try a different model and you edit the same file. Bugs follow.

The refactor: decoupling work for AI coding agents

Separating the concerns makes the code easier for you to manage, and easier for your AI coding agents to work on without collateral damage. The contract-based version:

class S3DataLoader:
    def load(self, path):
        # Only handles S3 logic
        return "raw_data"

class TransactionsCleaner:
    def clean(self, data):
        # Only handles domain cleaning
        return "cleaned_data"

class XGBoostTrainer:
    def train(self, data):
        # Only handles the model
        return "model"

class ModelPipeline:
    def __init__(self, loader, cleaner, trainer):
        self.loader = loader
        self.cleaner = cleaner
        self.trainer = trainer

    def run(self, path):
        data = self.loader.load(path)
        cleaned = self.cleaner.clean(data)
        return self.trainer.train(cleaned)

Swapping S3 for Azure now means writing one new loader class. The orchestrator never finds out. Code shaped like this survives a change instead of collapsing under its own weight.

2. Speculative generality: the YAGNI trap

Divergent change builds up in old code. Speculative generality is there from day one, when you or the agent try to future-proof against features nobody has asked for. I call it the “monster project” smell. A vague prompt like “make it scalable” is all it takes, and AI coding agents will return hundreds of lines of abstract classes that nothing ever implements.

The rule here is YAGNI, You Ain’t Gonna Need It. Instead of a “universal model trainer,” write the simplest thing that works and let the design grow when a real requirement shows up. Organic growth holds together better than architecture you guessed at. If hallucinations are the bigger problem at this stage, I wrote a separate guide on AI coding agent context.

Context is the thing the agent does not have

Software engineering is rarely about whether the code is “correct.” It is about context, and the agent has none. It cannot tell a throwaway MVP from a multi-million dollar revenue engine. Your value as a data scientist sits in structural intuition: knowing when something needs refactoring and when leaving it plain is the right call.

I have also written about the cost of vibe coding and what it does to site stability.

If cleaning up after AI coding agents is eating your dev hours, hand it over. I have been working with WordPress since the 4.x days and I know how to keep a codebase clean.

Stay the architect

Learn the code smells, and learn where abstraction earns its keep. Let AI coding agents write the boilerplate and keep the structural decisions for yourself. Nothing on the agent’s side is steering, and it will happily produce unmaintainable legacy code at speed. Ship it, but ship something you can still read in six months.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.