What I actually learned by working outside Lovable
I want to tell you about the moment I realised I'd been building blind. I'd been using Lovable for months. The app was working. Features were shipping. I felt like I was building. Then I moved to a local stack, opened my terminal for the first time, typed psql and connected directly to my database — and ran a simple query to check whether my new user signup flow was actually saving profiles.
By The Non-Developer Developer
I want to tell you about the moment I realised I'd been building blind.
I'd been using Lovable for months. The app was working. Features were shipping. I felt like I was building. Then I moved to a local stack, opened my terminal for the first time, typed psql and connected directly to my database — and ran a simple query to check whether my new user signup flow was actually saving profiles.
sql
SELECT * FROM profiles;Empty.
The UI had been showing success messages for weeks. The data wasn't there. I had no idea.
That was the moment I understood what working outside Lovable would actually teach me — not just how to use different tools, but how to see what was happening inside my own product.
What Lovable was hiding
I want to be clear: I'm not criticising Lovable. It's genuinely excellent at what it does, and I still use it for frontend work. But when Lovable handles everything, it also hides everything.
Errors become "something went wrong." Database issues become "that feature isn't working." Deployment problems become "it was working yesterday." You're debugging against symptoms, not causes.
Moving to a local setup - GitHub, terminal, SQL, Chrome DevTools - didn't just give me more tools. It gave me visibility. And visibility turned out to be the thing I'd been missing the whole time.
GitHub - your code finally has a history
The first thing that changed when I connected properly to GitHub was that I stopped being afraid of breaking things.
Inside Lovable, a bad change can be hard to undo. You're hoping the AI can reverse what it did, or you're clicking through version history that you don't fully control.
With GitHub, every change is a commit. Every commit is reversible. Every feature lives on its own branch. If something breaks, git reset takes you back. If a feature makes things worse, the branch gets deleted and main is untouched.
That safety net changed how I work with AI tools completely. When Claude Code or Codex makes a change I'm unsure about, I can let it run because I know I can get back. When I review a PR before merging, I'm seeing exactly what changed, every file, every line, diff format, nothing hidden.
The commands that changed my workflow:
bash
git checkout -b feature-name # new branch for every feature
git add . # stage changes
git commit -m "what this does" # commit with a clear message
git push origin feature-name # push branch, Vercel creates preview URL
git log --oneline # see what happened
git diff # see what changed before committingNone of these are hard. But knowing them means your codebase has a memory, and you can always find your way back.
The terminal - less scary than it looks
The terminal was the thing I avoided longest. It felt like the line between "building with AI tools" and "being a developer." Then I realised that the commands I actually needed were about twenty, and most of them I'd be copy-pasting from the docs anyway.
What actually shifted my relationship with the terminal was understanding that it's just honest. The terminal tells you exactly what happened. No success animations when something failed, no swallowing errors to keep the UI clean. If npm run dev fails, it tells you why. If a migration doesn't apply, it shows you the exact error.
The commands that come up constantly:
bash
npm install # install dependencies
npm run dev # start local dev server
npm run build # build for production
npx tsc --noEmit # check TypeScript types without building
supabase login # connect to your Supabase account
supabase link # link to a specific project
supabase db pull # pull schema as migration files
supabase db push # push migrations to Supabase
git status # what's changed
git stash # temporarily save uncommitted changesThe moment the terminal stopped feeling hostile was when I started reading its output instead of closing the window. The error is almost always there, in plain text. You just have to look.
SQL and psql - actually seeing your data
This is the one that changed the most.
Before psql, my understanding of my database was: "I think the data is in there." After psql, it was: "I can see exactly what's in there, what's missing, and why."
Connecting to your Supabase database from the terminal:
bash
psql "postgresql://postgres:YOUR_PASSWORD@db.YOUR_PROJECT_REF.supabase.co:5432/postgres"Then you're in. The postgres=> prompt. And suddenly your database is a thing you can interrogate directly.
The queries I use constantly:
sql
\dt -- list all tables
SELECT * FROM profiles LIMIT 10; -- check if data is saving
SELECT COUNT(*) FROM profiles; -- how many rows exist
SELECT tablename, policyname, cmd
FROM pg_policies ORDER BY tablename; -- check RLS policies
SELECT * FROM auth.users LIMIT 5; -- check if users are being created
\q -- exit psqlThe RLS policy query became part of my standard debugging routine. Half the times I thought something wasn't working, it was a missing write policy. The data was being blocked at the database level, silently, with a success message showing in the UI.
Once I could see that directly, fixing it was trivial. Getting there was the hard part — and the only way to get there was to look at the actual database.
Chrome DevTools — what's actually happening in the browser
Chrome DevTools was the other visibility shift. Before I used it properly, I was debugging by looking at the UI and guessing. After, I could see exactly what requests were being made, what data was coming back, and where things were going wrong.
The two panels that matter most:
The Network tab. Every request your app makes shows up here. You can see the URL it called, the status code it got back (200 = success, 400/500 = something's wrong), and the exact response. When a Supabase query returns empty results, the Network tab shows you whether the request even succeeded and what it returned. More often than not, the problem is visible right there.
The Console tab. JavaScript errors, failed imports, unhandled promises — they all show up in the Console. When something isn't working and there's no obvious UI error, the Console usually has the actual message. It was in the Console that I first saw TypeError: Cannot read properties of undefined and learned what that actually means.
Opening DevTools (F12 or Cmd+Option+I) and leaving the Network and Console tabs open while testing became a habit that probably saved me more debugging time than any other single practice.
Understanding the code structure
Working in a local codebase taught me something Lovable never could: how everything connects.
In Lovable, you describe features and they appear. In your local repo, you can see that a button click calls a function, that function calls a Supabase query, that query hits a table, that table has RLS policies, those policies check the user's auth status. The whole chain is visible.
That visibility compounds. Once you understand that chain for one feature, you understand it for all features. Once you've traced an error from the UI down to the database and back, you know roughly where to look next time something breaks.
The structure of a React + Supabase project is surprisingly learnable:
src/components/— the UI piecessrc/hooks/— the data fetching logic (this is where most bugs live)src/integrations/supabase/— how the app talks to the databasesupabase/migrations/— the history of your database schema
Claude was invaluable here. When I opened a file I didn't understand, I'd paste it in and ask: "Explain what this file does and how it connects to the rest of the app." The explanation would be plain English, specific to my code, and usually taught me something I'd use again.
The confidence shift
Here's the thing that surprised me most: I'm not a developer now. I still rely heavily on Claude Code and Codex to write actual code. My understanding of TypeScript is functional at best.
But I'm not building blind anymore.
When something breaks, I have a process. Check the terminal output. Check the Console. Check the Network tab. Check psql. Ask Claude to explain what I'm seeing before asking it to fix anything.
That process means I understand my own product in a way I never did inside Lovable. I know what's in my database. I know what requests my app is making. I know where to look when something goes wrong.
That knowledge compounds. Every debugging session teaches me something. Every SQL query I write and run and understand makes the next one easier. Every git commit I review makes the codebase less mysterious.
Working outside Lovable didn't make me a developer. It made me a better director of the AI tools I use. And that turns out to be exactly what I needed to be.