About this talk
This talk explores the integration of AI in front-end development using React, focusing on the concept of vibe coding. The speaker, Adar, who is the co-founder and CTO of his company, shares his experiences from building an internal tool that utilized vibe coding with React, TypeScript, and Tailwind. He discusses the challenges faced, including code duplication and mismatched abstractions, which arise when AI-generated code lacks a cohesive architectural context. Adar emphasizes the importance of maintaining a clear understanding of code structure, especially as non-developers contribute to projects. He concludes by proposing a sandbox isolation approach for components, allowing designers and product managers to work autonomously while maintaining developer oversight of data flow and application structure.
Full transcript
All right. Hello everybody. Today we're going to talk about what's your AI written front end look like with react. A few perspective of mine about what's going on when you start putting vibe coding inside your react applications. So my name my name is Adar and I'm a co-founder and CTO of my app.dev a company willing to let you ship vibe into production in a safe and easy
manner. Before that I was at the Wix for almost 7 years. My last role there is the head of front end guild managing the entire front end operation and development technical stack of um the actual front end. Overall [snorts] 15 years in in this field focusing on building front end infrastructure. it started 6 months ago when we start when we wanted to build an internal tool for
team retrospective inside our company. Pretty straightforward a few screens few thousands lines of code nothing special react typescript tailwind and we decided to start vibe coding it before we start doing it on our real products. So we gained the the knowledge and see all the risks and benefits. And we did everything right. Um Like we put the same team. We used the same review discipline. We review
the pull request just like any other code. We use cursor then we move to cloud code standard. Um surprisingly or not surprisingly by by today our PM and product managers, the designers joined to the effort and start contributing. Uh when vibe coding is so easy, why not? >> [snorts] >> But after few weeks, nobody really have a clear understanding of what's going on in the system. If
you took a a bird's eye of the code, it's look like a mess. The code base is actually drifted. And in this presentation, I want to show you a few examples of what what is drifted and the mitigations that we to be helpful. This talk is not about bugs, it's about again, how your code going to look like even if the type script going to be strict,
if the test is passed, if the lighthouse score in the performance is good. Um but to keep an eye on the architectural. So, I want to start by few what we call the fail modes, like things that when you are writing the vibe code and the agents do, line by line code file by file, it's look correct, but then there is more behind the knif. So, one
thing is a duplication of code, right? And the lack of a reusability. Here I have three different example examples from our applications of debouncing debouncing user inputs. Each of them by itself is right, it's correct, it's valid. Use debounced self-implementation of of React hook, right? >> The second one is just use effect with set timeout and clear timeout. And the other way and the other option is
importing the debounced from a lodash. All of them, one by one, is valid, right? Test pass, application working, performance is okay. But three of them together is a really smelly smelly code. We ended up actually even using uh two instances on the same file, right? Two different AI sessions. Uh we have two of them inside our our application. we ended up like this. Second fail uh fail
mode that we we see repeating itself again and again is a mismatch mismatch abstractions, right? Uh the AI is usually getting out of context. For example, look at this really nice button. A reusability button with all the feature like alignment and loading and size capabilities like 23 props. In reality, it using the application only once with only three props relevant, right? Continuing in this design system and
reusability stuff, we ended up by implemented four different primary buttons. Looks close to each other. All of them following the design system principle. All of them are following the Figma. All of them are look correct and right. Uh >> but if we now want to do any kind of a change, we need to change in four or five different places, right? they have slightly different borders, slightly
different fonts, slightly appearance. And the point here is that even although we have like this design system button, different AI sessions didn't use it, right? It just created it again and again. Uh [snorts] the AI cursor and cloud code by itself never created like the theme theme TS or common place to put the tokens, the design constant to create a shared color map. the design system button
never used. And the idea here and it's a repeated repeated pattern. When we ask make this screen looks good or make it follow the Figma, even if we connected to Figma MCP, even if we're just pasting guidance again and again, every session interpreted what is good individually, right? And we can end it up with a different implementations for the same requirement, for the same features. Especially especially
when non-developers are contributing to the code with us. Again, think about the designer, the product managers that continue to contribute that come in and continue and contribute to the product. And we as the reviewers, you know, we're getting a PR. Each of them in the PR looks fine, right? We need to understand and and and maintain the mental model about what already exists and what not. And
with the faith and the speed of the AI, it's barely impossible. The fourth fail mode that repeat itself is that components that get bigger and get lost its consistency and actually broke. For example, this component, the main dashboard, used like states, few elements of of states like the cards, the columns, the votes, the is editing mode, right? Um and we implemented this use effect every time that
the user click a vote on some element, we want to optimistically optimistically update like the pending the pending action to the server. >> And once we get the result from the server to actually update the vote count for each card. >> Um it used it used to work at the beginning, then in some cases it say fell. One of the developers try to investigate. As you can
see here the the AI put a comment to do flaky flaky action cannot reproduce locally. And when and if we are taking like a higher view and look at the code again, every piece here is making sense. We had like the handler vote whenever they use the click the card. So we want to set the votes and set the optimistic update value and then call the the
server, right? Um, fetch to the server to to update the the card ID. Then we have two user effects to clear the optimistic update after 2 seconds, right? Which is an assumption that the AI took that it's never going to take more than 2 seconds for its server to to react. And then once the server responds, we're gating we're gated the the handling by check if there
is optimistic vote in order to try and gain a performance on to or to um, not rerun set set states again and again with with not needed. But actually it causes a bug in production, right? Because every piece of this code generated in a different session. The AI never took a higher view and understand the bigger picture. No single line here is is wrong, but the entire
picture the entire composition is broken. And as I mentioned before, like we had tests. The tests are are passed and we need to remember when we let AI doing everything when we are vibe coding and especially where non developer is doing that, test passed is actually saying is nothing because the model that generate the test generated them in the same mental model that you wrote the code,
right? So, tests that you are generating just after creating the feature, and again, especially if we're not knowing to check it and to test it manually, is worth nothing. And this is exactly the case here. So, how we can overcome it? And what we actually did in our company, in our dev team. So, we have several options, and I'm sure that for some of you, it's it's
obvious, and you probably already did some of them. We want to start giving our model and vibe coders more context, architectural context, rather than just generate the code again and again from scratch each time. So, tools like Agent MD, Claude MD, cursor rules, right? These are all context that we can put and commit into the repo, which mean that if we learn something, if we implement something,
we can put it inside the repo, so other vibe coders, other agents in other sessions can use. For [snorts] example, use debounce from and define exactly where the debounce is defined. Validating things like do not re-implement validations, or do not implement the same regex validation in four, five different places. Same as the buttons, right? Define our design system in a committable file that be accessible and readable
to the agents. Without it, every AI session start from blank. Every AI session can do whatever it want, the way he sees and understand it. on top of that, enforcing rules and and structure by existing tools. Uh lint, right? Uh ESLint, the file on config, uh strict specific imports, or maintain an import allow list, right? if you know if you if you're if you're able to break
the build or to fail the CI in certain cases, so for sure you're the AI can overcome it and suggest a fix. And always keep in mind that if you're not already there, really really soon even non-developer going to contribute to your project, right? Uh the entire things is around that. Review interactions and not lines of code. Again, all the example that I showed you in the
PR phase it at the code review phase, they passed. The code was was valid. Um it's not is the use effect is well written. Is what this use effect is is doing relative to other use effects, relative to other async operations, or things that happening in the and something that we found ourselves doing in order to try to automate this process uh this process is to paste
this prompt after each change. Actually ask the AI after each time, all right, what you change, which new imports you use, now go and scan the project and try to understand if there are duplications or you can reuse some other library that already doing it. it's kind of an habit that we found doing, which improve all of those things improve some of it. All the failures that
I showed you have a single root cause. The surface of an entire application, and applications become bigger and bigger, the The of a code, the the phase of delivery software become bigger and bigger with the AI and the context, the scope that we are giving our agents to run on to to run on become too big to hold in one session. It's right for human and it's
right for AI. And then it caused those drifted. And we start think to ourselves how we can an architectural decision, architectural solution to smaller those areas to let work in a boxes or in contexts that are that are much more self-contained. strong contract much better to understand and to to change the code inside of them. We thought about way to run time composition, separation of concern and
to make it strong boundaries. Um of course we look into the common solutions out there which we probably all know, the model federation approach, micro front end, web components. They're all great and they're all aiming for us as developers. Remember, we look for a way to involve non-developers, designer, vibe coders and we look to for a way to minimize the PR time and the review that we
need to on this generated code. each of those solutions give us some improvements. they're not full run time isolation. They still can contribute errors, change globals, change application state. Uh and we still need to check them manually and review the code. Remember, the the designer of the PR the the designer of the PM can still reach outside the boundary. Can still ruin stuff in the application. So,
we took a different path. what I'm here to to share with you today. >> We implemented like a full sandbox sandbox isolation for components. A way that the vibe coder can use the existing tool, the cursor, the cloud code, and just vibe code in a sandbox. You control the UI, you control the interactions, you control everything that you want, but you rely on a data that we
as developer pass in, and we are reacting only to events to specific event that we agree upon. while defining the contract. the agent and the vibe coder itself have no access on runtime and on build time to the repo, to the structure, to outside the boundary of the component. The mental model is something like that. Inside a component, you have access and full autonomy to change the
UI, the layout, the copy, the animation, UI, UX, do whatever you want. And this is actually your profession, right? You are a PM, you're a designer. Do whatever We have to sit and agree one once on the runtime contract. Data gets in, events send out to the application. >> And for us as a developer, now we are much more in control. We are the one that fetching
the data, mutating the data, syncing between server and and front end, handling the events, the routing, the authentication, but we have a full understanding and a full control and autonomy about the data flow and we know that it's not going to change while the code inside the components in most of the cases are not worth reviewing, right? The designer create a component which have access to nothing
but the UI, it can get just go to QA. It can just go to the CI pipeline and if all good, performance is good, it can be shipped to production. the scope of what we are checking minimize into what we actually want to check, all the things on the right. Neither side need to test the other one. We didn't need to review the stuff and the vibe
coder, which is not developer, don't need to understand anything from He shouldn't understand event handling, fetching data, structure of object, things that in in any case is not really understand. Remember, AI is a multiplier of your architecture. If you put no architecture in place, it will invite something and it will invite something else each and every time. If you put something strong, AI will multiply it and
use it the right way that you choose. Review the interactions and not the file and not and not the files. If you want to learn more, reach to me in LinkedIn or read about our company. Thank you very much. >> [applause]