Vibe Code Review
In my previous article (Continuing the Vibe), I shared my latest experimentation with the vibe coding platform, Lovable.
I created a simple, single-purpose web application to support my son with his spelling education.
To help appraise the quality of the code produced by a vibe coding platform, I took the codebase and completed an end-to-end automated review using Cursor.
A summary of the findings can be found below.
Critical Security Issues
- Unsafe random in
Test.tsxline 139:const shuffled = [...allWords].sort(() => Math.random() - 0.5);. Not cryptographically secure and can be biased. Usecrypto.getRandomValues()or a Fisher–Yates with an RNG. Also reviewsrc/components/ui/sidebar.tsxline 536. - Missing env validation in
src/integrations/supabase/client.ts:SUPABASE_URL/SUPABASE_PUBLISHABLE_KEYcan be undefined. Add startup checks; consider a guard/constants module. - Overly permissive RLS policies. After migrations, public read/write on users, words, test_scores, and word_attempts is risky.
- No user input sanitization beyond
.trim(). Add validation and allowlists. - Authentication/client-only auth relies on localStorage; logouts don’t invalidate server-side sessions.
Code Quality and Architecture
- Duplicated user management across
Home.tsx,Dashboard.tsx, andTest.tsx. Extract a shared hook or component. - No error boundary for app-wide crashes. TypeScript strict settings (
noImplicitAny,strictNullChecks, etc.) are off; risks unknown-typed code. MissingQueryClientdefaults for refetch, stale time, retries. - Missing loading skeletons in
WordPerformanceHeatmap; only plain text. - Unused route
Index.tsxwith placeholder content. UnusedApp.css. - Race conditions in
Test.tsxline 179 via nested async aftersetTimeout; preferasync/await.
Performance
- No memoization: heavy list/grid renders and inline functions; query results not memoized.
- Unbounded lists: words, users, scores, and attempts load fully. Add pagination/cursors.
- Possible loop:
Home.tsxline 127:loadUsers()on dialog open; enforce a cooldown or debounce. - Large state: full word/user arrays kept in memory with no streaming.
- UI queries are parallelizable where missing; e.g.,
Dashboard.tsxlines 108–136. - Supabase
order()without indexes; verify existing indexes.
Reliability and User Experience
- File upload in
WordManager.tsxaccepts any text; enforce a schema and size limits. - Orphaned records: deleting a user leaves scores/attempts; inconsistent dashboard totals.
- Race conditions in auto-creation: double inserts if users create concurrently.
- No conflict/rate-limiting handling (e.g., duplicate usernames; concurrent test submissions).
- Missing network retries; toasts lack deduplication.
- Inconsistent error handling (some toasts, some logs, some silent).
Positive Observations
- Clean migration structure.
- Useful indexes and foreign keys.
- Decent component organization.
- Solid UI patterns.
- Working RLS setup (policies need tightening).
- Broad test coverage.
Overall, Cursor provided the following scores.
Security Score: 4/10
- Overly open RLS, no rate limiting, weak input validation, and no authentication.
Code Quality Score: 6.5/10
- Duplication and missing boundaries; solid structure and user interface.
Performance Score: 5/10
- Missing memoization and pagination.
Overall Score: 5/10
- Needs tighter security, deduplication, pagination, and strict typing.
In conclusion, I believe this is a fair review and score. The security score is a little misleading, as I had purposely not included user authentication, recognising the use case and target audience. Therefore, the score of 4/10 reflects reality, but is an expected outcome (part of the design).
These results validate my position that vibe coding is very well positioned for rapid experimentation and the development of single-purpose web applications for personal use (where there is minimal risk).
However, when considering the use of vibe coding for business or commercial use cases, I would highly recommend a thorough peer review, with a focus on security and performance at scale.