You're using Lovable or Bolt to build your app. Something breaks. The AI says it fixed it. You click around and it's still broken. You ask again. It says it fixed it again. The cycle continues until you're twelve prompts in and the app is somehow worse than when you started.
The missing move, the one most non-technical founders skip, is looking at the code yourself. Not to write it. Not to understand every line. Just enough to see where the problem actually is.
Reading code is not the same as writing code. You don't need to understand how something was built to see that a variable is named undefined, that an API key is blank, or that the wrong page is being loaded. Those are literacy skills, not programming skills, and you can pick them up in a few hours of deliberate practice.
Here's where to start.
The five files in any indie app that explain most of what it's doing
When Lovable or v0 generates a project, it creates dozens of files. Most of them are scaffolding you'll never need to touch. The ones worth knowing are:
1. The main page file. Usually called App.tsx, index.tsx, or named after the feature (like Dashboard.tsx). This is where your app's visible structure lives. If something is showing up wrong on screen, it's probably here.
2. The component for the broken thing. If a form isn't submitting, the component that renders that form is the first place to look. Lovable names these predictably: a login form lives in LoginForm.tsx, a pricing page in PricingPage.tsx. Look at the file that matches the broken feature.
3. The API or data file. Often called something like api.ts, supabase.ts, or hooks/useData.ts. This is where the app reaches out to the internet to fetch or save information. When data isn't loading, this file is usually involved.
4. The .env file (or .env.local). This is where your API keys and configuration live. It's a plain text file. If the app behaves correctly locally but fails when deployed, the first thing to check is whether your environment variables are set correctly in both places.
5. The router file. This tells the app which URL shows which page. If clicking a link goes to the wrong place, or a page is blank when it shouldn't be, check the router. In React-based tools it's usually in a file containing the word routes or router.
You don't need to read these files deeply. You need to know they exist and where to find them. Once you can open the right file for a given problem, you've already narrowed the search space from "the whole app" to "this one area."
"Ask the AI to explain it back to you" is the debugging move most people skip
When the AI makes a change you don't understand, most people do one of two things: they accept the change because it seems to work, or they reject it because it looks foreign. Neither is the right response.
The right response is to ask: "Explain to me in plain English what you just changed, and why you think that will fix the problem."
This prompt does three things at once. First, it forces the AI to reason through its own output, which sometimes catches mistakes the model didn't flag. Second, it gives you a reference for the next time a similar bug comes up. Third, it tells you whether the fix is treating the symptom or the actual cause.
If the explanation sounds like "I updated the component to handle the state better," that's not an explanation. Push back: "Be more specific. What state? What was it doing before? What is it doing now?"
A good explanation sounds like: "The form was reading the username from user.name, but the API returns it as user.display_name. I changed the form to look in the right place."
That explanation tells you exactly what was wrong, where it was wrong, and why the fix is correct. If you get that, you can verify it. If you get vague language, the AI is guessing.
This is also the move to reach for when you're stuck in a loop where the AI keeps suggesting the same broken fix. Asking for an explanation often surfaces the assumption the model has been operating on, and that assumption is usually wrong.
Three patterns that explain most bugs you'll run into
If you read enough bug reports from Lovable and Bolt users, a few patterns come up again and again. Knowing the pattern helps you describe the bug more accurately to the AI, which gets you better fixes faster.
Pattern 1: State not updating. In web apps, "state" (the current values the app is tracking, like whether a modal is open or what's in a shopping cart) has to be explicitly updated. It doesn't just change because the underlying data changed. When something should update but doesn't, the most common cause is that the code changed the data but didn't tell the UI to re-render with it. Symptom: clicking Save appears to do nothing, but if you refresh the page the change is there.
Pattern 2: API call failing silently. An API (a way for your app to talk to an external service, like a payment processor or database) can fail without showing the user any error. The app just... doesn't do the thing. Symptom: a button does nothing, a page loads but is empty, a webhook never fires. In this case, ask the AI to "add a console.log after every API call that prints the response and any error." That surfaces the failure.
Pattern 3: Conditional rendering wrong. A lot of UI bugs come down to a condition that evaluates wrong. The code says "show this section IF the user is logged in" but the check is looking at the wrong thing, so the section is always hidden, or always visible. Symptom: a section shows up when it shouldn't, or disappears when it shouldn't. Ask the AI: "Walk me through the conditions that control when this section shows up."
Naming the pattern when you report the bug changes the conversation. "The save button doesn't work" gets you a guess. "The save button appears to succeed but the change doesn't appear until I refresh, which sounds like state isn't updating" gets you a targeted fix.
When to copy the error message into the AI vs when to read it yourself first
Error messages feel intimidating at first. But most of them follow a simple structure: here is what failed, here is where it failed, and sometimes here is why.
The last line of an error is almost always the most useful. Everything above it is the chain of code that eventually caused the problem. You can ignore the chain. Read the last line.
If the last line says something like TypeError: Cannot read properties of undefined (reading 'name'), that translates to: the code tried to read a property called name from something that didn't exist. That narrows it down immediately. Ask the AI: "What is this code trying to read name from, and why might it be undefined?"
The times to just copy-paste the error directly into the AI, without reading it first:
- The error is in a library you didn't write (long stack traces with unfamiliar file names)
- The error is about a build process or deployment configuration
- The error message is genuinely cryptic and you've already tried to parse it
The times to read the error before asking:
- It's a clear English sentence describing what went wrong
- It mentions a file you recognise
- You've seen this type of error before
The goal isn't to become someone who can diagnose every error cold. It's to get good enough at reading errors that you can give the AI a useful starting point instead of just "it's not working."
The "don't apply this fix until I understand it" rule, and why breaking it costs you days
Here's a habit that will save you a lot of pain: before you apply any AI-suggested fix to a part of the codebase you don't understand, ask for an explanation first.
Not after. Before.
The reason is compounding. When you apply a fix you don't understand, and it works, you move on. Great. But then the next bug appears in the same area. And now the AI has to reason about the code with the first fix baked in. If the first fix was subtly wrong, or if it traded one problem for a latent one, you won't know it. The AI won't flag it. It'll just silently make the second fix harder.
Three or four iterations of this and you're in a situation where the code has accumulated enough patches that the AI starts fighting itself. Fixes break things they shouldn't touch. Straightforward features start taking ten prompts. The AI starts contradicting its own earlier decisions.
The understanding requirement isn't there to slow you down. It's there to keep you oriented. If you can't explain in one sentence what a fix did and why, you haven't got enough signal to know if the fix was correct. And the AI won't always tell you when it's guessing.
The practical version of this rule: after every fix, write one sentence in a notes doc. "Fixed state not updating in the cart by calling setCart() after the API returns." That sentence takes thirty seconds. It also means that when the cart breaks again next week, you have context instead of starting cold.
The last thing you need to know
Reading code as a non-developer is a skill with a steep early curve and a shallow one after that. The first five hours feel like reading a foreign language. By hour ten, you're pattern-matching and asking better questions. By the time you've shipped your first real app, you'll know which files to open first without thinking about it.
You don't need to close that gap alone. The best move is to use the AI as a tutor, not just a fixer. "Explain this file to me like I've never seen code before" is a valid prompt. "What is this function trying to do?" is a valid prompt. "Why would someone write it this way instead of another way?" is a valid prompt.
The AI built the code. It can teach you to read it. You just have to ask.
Draftlytic won't teach you to read code, but it will give you a spec you actually understand before the AI writes a line, so you go into your build knowing what you're supposed to be reading for. When the cart breaks, you'll know whether it's a payment feature or a state problem or something in the API, because you planned it before it was code.