The Problem With Vibe Coding Isn’t the Code. It’s Where You Put It.

How we integrated an AI-assisted ROI calculator into a production website without letting an experiment become technical debt.

  • AI-Assisted Development
  • iframe
  • Software Architecture
  • System Integration
  • Technical Debt
  • WordPress

Vibe coding is about to create a lot of very real technical debt. And it’s probably your fault.

But not because the tools are the problem.

The opposite, actually.

Tools like Base44 are making it easier for people to build useful calculators, demos, microsites, portals, dashboards, and internal tools without waiting for a traditional development cycle. That’s a good thing. An idea that might have spent months in a backlog can suddenly become something people can use.

The problem starts when that experiment becomes a production system before anyone decides where it belongs, how it should be integrated, or who is responsible for keeping it running.

Generating the application is getting easier. Owning the application isn’t.

The two bad choices

I think a lot of teams are about to approach AI-assisted development as if there are only two options.

The first is to throw the experiment away and rebuild it properly. Take the useful prototype, write a conventional specification, and start over inside the existing codebase. Sometimes that’s necessary. But doing it automatically can erase the speed advantage that made the experiment worthwhile in the first place.

The second is to absorb whatever was generated directly into production: its markup, styles, scripts, dependencies, and assumptions. Now the experiment is part of the main website, whether anyone intended that or not.

Neither should be the default.

There’s a middle ground that developers have been using for years: contain the application, then integrate it deliberately.

An ROI calculator that didn’t need to become another website

We recently worked through this with Alkira, integrating an ROI calculator into its existing marketing site.

The calculator had been built in Base44. That was the interesting part. The business already had a useful application through a much faster, more experimental process than a conventional production build would have allowed.

What it needed was a home.

The initial result was closer to a standalone website than an embeddable application. It came with its own surrounding structure: header, footer, and the other elements you’d expect on a complete page.

That’s fine when you’re prototyping in isolation. It’s not what you want when the tool needs to live inside an established marketing site with its own navigation, layout, content workflows, analytics, and lead-generation process.

The requirement wasn’t to put a second website inside the first one. It was to preserve the useful guts of the calculator and let the production website handle everything around them.

You can see both sides of that work: the original Base44 application and the calculator integrated into Alkira’s website. The integration—not the calculator’s origin—is what makes this a sustainable production feature.

The iframe is the boundary

We used an iframe to keep the calculator separate from the surrounding page.

That sounds almost disappointingly simple. Good. It should.

The calculator owns its own interface and calculation logic. The production page owns the website experience around it. Neither needs to become the other.

But an iframe alone isn’t an integration. It’s just the starting point. A useful production implementation still needs a way for the two sides to cooperate.

For this project, that included:

  • Responsive height management: The containing page adapts as the calculator’s content changes instead of trapping the experience inside an awkward fixed-height frame.
  • Cross-frame communication: The application and its host can exchange the information needed to coordinate their behavior.
  • Gated-flow handoff: The containing page passes its URL fragment into the embedded application, then clears the fragment from the parent page’s address bar. The form submission and unlock behavior sit outside this host-side snippet.
  • Analytics continuity: The calculator’s activity remains part of the larger website experience instead of becoming an unmeasured island.
  • Consistent presentation: Visitors experience one website, not a complete standalone application awkwardly dropped into the middle of another one.

That is the actual integration work. Not generating the calculator, and not rewriting it from scratch.

Who owns what?

Responsibility Owner
Navigation, page layout, and surrounding content Production website
Calculator interface and calculation logic Embedded application
Height changes and cross-frame signals Integration layer
Form gating and the larger visitor journey Production website and embedded application’s agreed handoff
Analytics coordination Integration layer and production website

This is a small architectural decision with an outsized effect. If the calculator evolves, the marketing site doesn’t need to absorb every implementation change. If the marketing site changes, the calculator doesn’t need to own its header, footer, or page layout.

What the integration actually looks like

The production-side code is refreshingly small. The page contains an otherwise empty iframe, and the host assigns its src when the document is ready. It also forwards any URL fragment to the Base44 application and then removes the fragment from the parent’s address bar without reloading the page.

iframe-initialization.js
document.addEventListener('DOMContentLoaded', function () {
    const iframe = document.getElementById('calc');
    const hash = window.location.hash || '';
    if (iframe) {
        iframe.src = 'https://alkira-colo-roi.base44.app/' + hash;
    }
    if (window.history.replaceState && hash) {
        history.replaceState(null, '', window.location.pathname + window.location.search);
    }
});

That fragment handoff is one piece of the gated visitor flow. It doesn’t implement the form itself, and it doesn’t tell us what the Base44 app does with the fragment. It’s the integration point on the marketing-site side.

The more important piece is the message bridge. The embedded app sends a few specific messages, and the containing page responds to each one:

host-page-message-bridge.js
window.addEventListener('message', function (event) {
    if (event.origin !== 'https://alkira-colo-roi.base44.app') return;

    if (event.data && event.data.type === 'setHeight') {
        const iframe = document.getElementById('calc');
        if (iframe) iframe.style.height = event.data.height + 'px';
    }

    if (event.data && event.data.type === 'analyticsEvent') {
        const eventName = event.data.eventName;
        if (typeof gtag === 'function') {
            gtag('event', eventName, {
                page_url: window.location.pathname,
                send_to: 'G-MP78XJMSMT'
            });
        }
    }

    if (event.data && event.data.type === 'scrollTo') {
        const targetId = event.data.targetId;
        if (targetId) {
            const el = document.getElementById(targetId);
            if (el) {
                el.scrollIntoView({ behavior: 'smooth', block: 'start' });
            }
        }
    }
}, false);

That’s the actual host-side handler, not pseudocode. Three message types cover the integration:

  • setHeight lets the calculator tell the parent how tall it needs to be as its interface changes.
  • analyticsEvent sends an event into the marketing site’s existing Google Analytics setup, attributed to the host page’s URL.
  • scrollTo lets the embedded experience ask the parent page to scroll to an element outside the iframe.

The origin check is important: the page doesn’t act on messages from arbitrary domains. In a more generalized version of this pattern, I’d also validate the expected message shapes, constrain height values, and check the sender against the actual iframe. But the point here wasn’t to invent a platform for embedding arbitrary applications. It was to establish a narrow, understandable contract for one real integration.

The production layer still has a job

There’s a tendency to think that once AI can create the working interface, the difficult part is finished.

For a standalone demo, maybe it is.

For an application that lives inside a revenue-generating website, not even close.

What happens when the calculator changes height halfway through an interaction? What happens when someone submits a form and the embedded application needs to respond? How do you forward events into the host page’s analytics setup without losing track of the visitor journey?

Those aren’t reasons to reject the experiment. They’re the things a production website is already supposed to handle.

The right answer isn’t always to pull the new tool into the CMS, rebuild it as native blocks, or make the entire marketing site depend on its internal structure. Sometimes the right answer is a deliberately narrow interface between two independently maintained pieces.

That’s not a compromise. It’s architecture.

Technical debt starts when nobody owns the boundary

The real risk with vibe coding isn’t that AI wrote the code. Developers have been integrating code they didn’t write for decades: libraries, SaaS widgets, third-party scripts, payment forms, marketing tools, and entire applications.

The issue is whether anyone has decided how that code fits into the system.

If an experiment is copied into production without that decision, its assumptions become everyone else’s problem. Changes ripple through unrelated parts of the site. Ownership gets muddy. Eventually nobody remembers which part is supposed to control what.

Containment doesn’t make all of that disappear. But it gives the experiment a defined place to live and a manageable way to interact with the rest of the site.

It also preserves something worth protecting: the reason the business used the faster tool in the first place.

We don’t need to punish a useful experiment for having been built quickly. We need to give it a sensible operating model.

Build quickly. Integrate deliberately.

I expect to see a lot more work like this over the next few months. Business teams are going to produce increasingly sophisticated interactive tools, and the temptation will be to either dismiss them as unmaintainable or ship them untouched.

Both reactions miss the opportunity.

Let people experiment. Let them move quickly. Let them discover what works before committing to a full production build.

Then make an engineering decision about where the result belongs.

Not “ship whatever the AI made.”

Not “dismiss it because AI helped make it.”

Wrap it. Isolate it. Integrate it properly. Measure it. Keep the system clean.

The boundary is what lets you keep the speed without turning one useful experiment into permanent architecture by accident.

Let’s build something
that holds up.