Do Not Let AI Build the Elephant in the Room

AI usually doesn't build the elephant in one move.

That's the dangerous part.

A project starts out with good intentions. The file structure makes sense. The responsibilities seem pretty clear. You've got a place for components, a place for state, a place for services, a place for utilities, maybe even a few notes explaining how things are supposed to fit together.

So far, so good.

Then the real work starts.

You add one more button.

You add one more mode.

You add one more export option.

You fix one thing that only happens in the desktop version.

You fix another thing that only happens in the browser version.

You make the panel a little prettier.

You add a fallback.

You add a helper function.

Then you add another helper function because the first one was almost right, but not quite.

And before long, one file has become the place where everything goes.

At first, that's convenient.

Later, it feels normal.

Eventually, it's the elephant in the room.

The elephant is built out of reasonable changes

That's what makes this kind of problem so easy to miss.

The codebase usually doesn't go bad because someone sits down and says, "Let's make this file impossible to maintain."

It happens because each individual change seems reasonable at the time.

The AI is already editing that file.

The function you need is already there.

The state variable is already nearby.

The UI condition is easy to add right where the rendering is happening.

The model found the existing branch and added another branch.

The project still compiles.

The feature works.

So you move on.

Then you do it again.

And again.

And again.

That's how the elephant gets built.

Not from one big stupid decision.

From a hundred little reasonable decisions nobody stopped to review as a whole.

AI makes this easier to do by accident

Human programmers have been creating oversized source files forever. This isn't some brand-new AI problem.

But AI can make it happen faster.

An AI coding assistant often works from the context it can see. If one file already contains several related pieces of behavior, the AI may keep adding to that file because that's the nearest plausible place to make the next change.

And to be fair, a lot of the time it's doing exactly what you asked it to do.

You asked it to make the button work.

You asked it to add the mode.

You asked it to support the new state.

You asked it to fix the bug.

Unless you also ask it to protect the structure, it may optimize for the immediate task instead of the long-term shape of the project.

That's where things start to drift.

A source file that started as a component becomes a component plus state manager.

Then it's a component plus state manager plus parser.

Then it's a component plus state manager plus parser plus export coordinator.

Then it has some platform-specific behavior in there too.

Then it's the file everyone is afraid to touch.

And now the elephant is standing in the room.

File size isn't the only issue

A large file isn't automatically bad.

Some files are naturally larger than others. Some files sit at important coordination points. Some files really do have a lot to do.

The real question isn't simply:

"How many lines are in this file?"

The better question is:

"Does this file still know what it is?"

A file should have an identity.

It should have a reason to exist.

It should be able to answer two basic questions:

"What belongs here?"

And:

"What doesn't belong here?"

When a file can't answer those questions anymore, it's become a junk drawer.

That's when size becomes a symptom of something deeper.

The file isn't just long.

It's lost its responsibility.

Why this matters for full-file replacement

I'm a strong believer in full-file replacement when working with AI-generated code.

Patching little pieces into existing files is one of the easiest ways to create duplicate routines, misplaced logic, partial fixes, broken imports, and strange inconsistencies. If I'm working with an AI assistant, I'd much rather have it return a complete replacement file that I can review, compare, and drop in cleanly.

But full-file replacement only works well when files stay manageable.

If a file grows to 2,000 or 3,000 lines, full-file replacement starts getting painful. It uses more context. It's harder to review. It's easier to miss unwanted changes. And it tempts you to go back to patching because replacing the whole file feels too risky.

That defeats one of the better safety habits in AI-assisted development.

So file structure isn't just about neatness.

It directly affects the way you work with the AI.

A well-structured codebase makes full-file replacement practical.

An overgrown codebase pushes you back toward risky patch behavior.

The other problem is design drift

The oversized file is only part of the problem.

The deeper danger is design drift.

At the beginning of a project, you may know where things belong.

UI behavior goes here.

Data loading goes there.

Export logic goes somewhere else.

Platform-specific behavior is isolated.

Shared helpers don't get buried in product code.

Business rules don't get shoved into visual components.

But if the project keeps growing without an occasional structural review, those boundaries blur.

The AI may add export behavior inside a visual component because that's where the button is.

It may add platform-specific checks inside general-purpose logic because that's where the error showed up.

It may add document assembly rules inside a preview surface because that's where the rendered result appears.

It may add one more condition to a large block of conditions instead of asking whether the whole thing should be broken into named pieces.

And the code may still work.

That's the frustrating part.

The code can work even while the design is drifting.

Worse, the design can drift without anyone consciously deciding that it should move.

That's the danger.

The project still runs, but the shape of it has changed.

The AI needs a map

One of the most useful things you can give an AI-assisted project is a living architecture note.

Not a giant theoretical document.

Not a corporate design bible nobody reads.

Just a useful map.

Something that says:

  • where major behavior lives
  • what each important folder is for
  • what should not go into certain files
  • where UI components belong
  • where state lives
  • where parsing or transformation logic lives
  • where export logic lives
  • where platform-specific behavior lives
  • where shared utilities belong
  • what the main workflows are
  • what files should be checked before making certain changes

That matters because every new AI session starts with partial knowledge.

The model doesn't automatically understand your project history.

It doesn't know which parts were hard-won.

It doesn't know which files are stable and shouldn't be casually changed.

It doesn't know which files are old experiments.

It doesn't know which behavior is shared across modes unless you tell it.

If there's no map, the AI has to explore.

And when AI explores a large codebase without a reliable map, it burns time, burns context, and starts making guesses.

Sometimes those guesses are good.

Sometimes they're not.

A living architecture note gives the AI a better starting point.

It also gives you a way to notice when the actual code no longer matches the intended structure.

Schedule structural reviews

The answer isn't to stop building features.

The answer is to schedule structural reviews as part of the workflow.

Not every day.

Not after every tiny change.

But often enough that you catch the elephant while it's still small enough to move.

A structural review is different from asking, "Does the code compile?"

It asks:

"Is the project still shaped the way we think it is?"

That review might start with something as simple as listing the largest files in the project.

Then ask:

Why is this file large?

Is it large because it has one large responsibility?

Or is it large because unrelated responsibilities have collected there?

Does this file mix UI, state, data transformation, export logic, platform behavior, and helper functions?

Are there repeated conditionals that should become named helpers?

Are there sections that should become components?

Are there functions that belong in services?

Are there utilities trapped inside application files?

Are there platform-specific branches that should be isolated?

Are there comments or documents that still describe the structure accurately?

Would a fresh AI session know where to make the next change?

Those aren't academic questions.

They're maintenance questions.

They're also AI productivity questions.

The clearer the structure, the easier it is to ask for safe changes.

Keep refactoring separate from feature work

One important rule is this:

Don't ask the AI to clean up the architecture and add a major new feature in the same step.

That's asking for trouble.

Refactoring is already risky because you're moving working code.

Feature work is already risky because you're changing behavior.

Combining the two just makes it more likely that something gets lost, renamed, simplified, duplicated, or subtly broken.

A better pattern is:

First, ask for a structural review.

Then ask for a staged refactor plan.

Then perform one stage at a time.

Then test.

Then update the architecture note.

Then continue with feature work.

That may sound slower.

In practice, it's usually faster than trying to move the elephant after it's grown tusks.

A practical review prompt

When a project starts to feel like it's drifting, I don't want the AI to start rewriting code immediately.

I want it to stop and look.

A prompt like this is enough to change the direction of the conversation:

You are reviewing this project for structural drift.

Do not add new features.

Do not rewrite code yet.

Look for files that appear to be taking on too many responsibilities.

For each overloaded file, explain what belongs there, what doesn't belong there, what should probably be moved, and what risks need to be protected if we refactor it.

Pay special attention to files that mix UI layout, application state, data transformation, export logic, platform-specific behavior, and helper functions.

Return a staged refactor plan only.

That last sentence matters.

Return a staged refactor plan only.

Don't let the AI start typing code before it has thought about the shape of the project.

Once the plan makes sense, then you can ask it to perform one stage at a time, return full replacement files, and update the project notes or changelog to say what moved and why.

That's the kind of discipline AI-assisted development needs.

Not because AI is bad.

Because AI is fast.

A fast assistant can make a mess faster than a slow one.

The answer isn't to stop using the assistant.

The answer is to give it a process that protects the project while still taking advantage of the speed.

Where the book goes deeper

In Real Programmers Use AI, I expect to spend more time on this kind of workflow discipline.

Not just prompting.

Not just which model writes better code.

But the working habits that make AI useful without letting it quietly damage the structure of the project.

That includes full-file replacement, repo snapshots, session-sized work, clear boundaries, architecture notes, staged refactors, and periodic structural reviews.

Because the real question isn't whether AI can help write code.

It can.

The real question is whether the developer remains in control of the shape of the codebase.

Don't wait until the elephant is full grown

The practical lesson is simple.

Don't wait until one file has become impossible to review.

Don't wait until every bug fix requires searching through half the application.

Don't wait until the AI has to rediscover the whole project before making a small change.

Don't wait until the code technically works but nobody understands where anything belongs.

Stop sooner.

Look at the structure.

Move responsibilities while they're still movable.

Update the map.

Then keep going.

The elephant doesn't appear by magic.

You build it.

One convenient shortcut at a time.

You can read more about my own experience with this in the related field note: The Elephant Does Not Walk Into the Room.

-- Charles