Show HN: I vibecoded a 35k LoC recipe app (recipeninja.ai)

126 points by tomblomfield ↗ HN
Over the last 2-3 weeks, I vibecoded the recipe app that I always wished existed - recipeninja.ai . It now includes a fully interactive voice assistant so you don't need to get your dirty hands over your new iPad when you're cooking.

Background: I’m a startup founder turned investor. I taught myself (bad) PHP in 2000, and picked up Ruby on Rails in 2011. I’d guess 2015 was the last time I wrote a line of Ruby professionally. Last month, I decided to use Windsurf to build a Rails 8 API backend and React front-end app, using OpenAI's realtime API for voice-to-voice responses. Over the last few days, I also used Claude Code and Gemini 2.5 Pro for some of the trickier features. 35,000 LoC later, this is what I built!

The site uses function-calling to navigate the site in realtime as you chat with the voice assistant, which I think is pretty neat.

For the long version, see https://tomblomfield.com/post/778601470234918912/vibecoding-...

I'd love any feedback you have!

Demo video of the voice assistant: https://www.youtube.com/watch?v=kRhVc9D5kcg

Generate and edit new recipes: https://www.youtube.com/watch?v=VwwZF6dHcHg

257 comments

[ 3.2 ms ] story [ 210 ms ] thread
This is great. Want to come demo it in the weekly EarthPilot.ai AI Playground tomorrow at 11est?

Http://earthpilot.com/play and then join at AnthonyDavidAdams.com/zoom at 11 for show and tell.

I’m making a non-fiction book writing agent and I’d love to better understand how you used function calling to navigate the website!

Basically you declare to the AI which functions (tools) are available for it to call: https://platform.openai.com/docs/guides/function-calling?api...

Then you handle those function calls in your javascript.

``` if (function_name === 'search_recipes') { const searchParams = new URLSearchParams();

      if (args.name) searchParams.set('name', args.name);
      if (args.difficulty) searchParams.set('difficulty', formatDifficulty(args.difficulty));
      if (args.min_duration) searchParams.set('minDuration', args.min_duration.toString());
      if (args.max_duration) searchParams.set('maxDuration', args.max_duration.toString());
      if (args.tag) searchParams.set('tag', args.tag);
      
      // Handle ingredients array correctly - the search page expects ingredients[]
      if (args.ingredients && args.ingredients.length > 0) {
        // Clear any existing ingredients
        searchParams.delete('ingredients[]');
        
        // Add each ingredient individually with the correct array notation
        args.ingredients.forEach((ingredient: string) => {
          searchParams.append('ingredients[]', ingredient);
        });
      }
      
      const queryString = searchParams.toString();
      const url = queryString ? `/search?${queryString}` : '/search';
      
      navigate(url);
      return;
    }
    
    // start_cooking function
    if (function_name === 'start_cooking') {
      // First check if we have an onStartCooking callback registered
      if (callbacksRef.current.onStartCooking) {
        callbacksRef.current.onStartCooking();
        return;
      }
    }
```
Afraid I have a day job, so I will politely decline your kind invitation
Thanks! I’m going to show the folks what you built. We have VCs, founders, and all Sorts of folks who join us every week.
Well this is a good start, I tried searching for “Anti-inflammatory” and the app crashed. Likely that scaling is way out of proportion considering it’s here on HN , but do you have any load balancing or caching setup? Debouncing on the inputs?
[flagged]
(1) It's incorrect to assume that the content published on HN benefits only the VCs, and none of the other participants.

(2) This particular post is an interesting data point in the research of what the current crop of LLM-based tools is capable of. It reads a bit like a Windsurf ad; I would like more details on how the technical side of the development panned out, what were the problems and where, how were they overcome, etc.

(3) The parent comment reads as a somehow funny mix of socialist "anti-greed" agenda and frowning upon the fact of sharing knowledge and experience freely.

I type "lasagne", hit enter, some things pop up and disappear before I can read them, and then I get a solid white screen. (in Firefox)
I still haven't done the AI vibe coding, but I think I get a similar effect with the Conjure plugin for Neovim with Clojure.

Being able to type out and immediately execute it directly in the window, and even have your code replaced by the output, is kind of life-changing. It fundamentally changes the way you write code, like the REPL isn't just a quick way to test your code, but a direct helper to test the stuff that you right.

I did a project in Clojure recently, heavily using Conjure, and then my next project was in Rust. Rust has nice Neovim plugins as well, but it still kind of felt like a step backwards; I found myself reaching for the "automatically evaluate" keystrokes that don't exist on Rust.

Is the key feature here just the voice control? I’m wondering what you feel is missing from other popular recipe websites/trackers, and why I would choose to use this over something with more care put into it.

Or, was this mostly just an exercise in engineering/testing AI?

Hands-free voice control and being able to access recipe ingredients and steps without 5 pages of SEO-optimised prose.
They just haven't done the "now monitise it" step in the vibe-coding journey.
with all respect to the man himself, not sure tom blomfield needs to think about that part..
It’s ok! He’ll just ask the AI! It’ll solve it for us!
The real problem of your website is that the recipes are AI generated.

A second, minor problem of your website is that the images illustrating recipes are AI generated with a bad quality

You can't solve those issues by throwing more AI.. well maybe the second problem you can (AI images with later models are generally ok)

Why would I want to do that when I can go to one of dozen AI chat interfaces and ask for recipes and further customization without having to look at any interface?
Would love to see a git repo
I'm scaling the heroku database right now, which caused a little bit of downtime :-/
Among Heroku and OpenAI bills, the project is apparently not optimized for low-cost operation just yet. Should it become popular, this may become an issue.
Impressive!

35 kLOC is quite a bit. I wonder how straightforward and maintainable this app ended up to be. This would require taking a look at the sources. While good Rails code tends to be very terse, frontend may be quite voluminous.

> I believe within a couple of months, when things like log tailing and automated testing and native version control get implemented

This sounds a bit too optimistic, especially around automated testing, but yes, eventually this all will be there.

> an extremely powerful tool for even non-technical people to write production-quality apps

But why would non-technical people would even think in terms of log tailing and version control, any more than they think about the gauge of wiring in their walls, or the kind of modulation their Wi-Fi device use? For really non-technical audience to make a good use of such tools, it won't just take the AI to be a competent coder. The AI should become a competent architect and a competent senior SWE to translate from the product management language to the software development language, without even surfacing it when not explicitly asked. It's going to be quite a bit of a challenge to make it work, and work about as reliably as with a human team.

Since I wrote the blog post, I actually added automated tests which Windsurf / Claude Code runs before committing any change. It saved me a lot of headaches when the LLM decided to make random logic changes for no reason.
Would you mind describing the process for this in a bit more detail?
The LLM swaps out business logic without you knowing it? As someone who worries about determinism and doesn’t do much vibe coding, I am curious what’s going on here, thanks!
Absolutely, very often. It typically happens because it's trying to do more than you asked for.

Example scenario: you have a codebase that you iterated on with LLM, and it contains let's say 15 features with various implementation details. You continue tomorrow and want to make a small change to handle an edge-case. While making changes to one of the 3 required files, suddenly it will decide to also rewrite / "improve" other parts of the code that have nothing to do with your request, where pieces of previous logic will no longer exist - since it made 18 changes to the file, and there are 3 such files, good luck spotting it without thorough and detailed change review.

Also, if you made manual changes to generated code and than you ask it to add something to the code (within the same "conversation" / context, it will often replace your changes with how it originally wanted code to look like.

I experienced this, too. It is quite annoying. You have to explicitly tell the LLM to leave everything else as-is.
use conventions.md to have it not do that.
Do things like Cline's memory bank solve this?
"Let's take a different approach. Instead of trying to fix the existing file incrementally, let's rewrite it completely with a simpler approach that uses the --print-config flag to test the configuration:"
Yeah, without trying to be dismissive I'm a bit unsure where the 35kLOC are going?
The vibes? It was vibecoded.
probably includes npm packages. Actual loc is probably 2-3k.
with npm packages it's probably 35k - Files -
> 35 kLOC is quite a bit. I wonder how straightforward and maintainable this app ended up to be. This would require taking a look at the sources. While good Rails code tends to be very terse, frontend may be quite voluminous.

I think people will have to recalibrate on this. The LOCs do things that you otherwise would not do. Features and details that simply would not happen — because they are too code/time intensive for most projects. It just won't matter anymore.

> But why would non-technical people would even think in terms of log tailing and version control

They won't! They won't have to. The obvious good stuff that everyone thinks the AI tool should be able do, will just work, because the people building the tools, will mostly obviously focus on making them work.

That or we're completely ignoring principles like DRY and producing a head spinning amount of tech debt.

I can't really imagine producing that much code in that short amount of time and holding any amount of it in my head. I'd bet money there's code in there that does the same thing but different, leading to all kinds of little inconsistencies that make this code worthless in any serious context.

I’m an old school coder. I haven’t vibed. I’m guessing the “who is going to maintain this” could only be another tool similar to what built it. I can’t imagine trying to get my head around that much code in a timeframe acceptable to someone demanding product. So are we headed to a place where everything is written and maintained this way or a lucrative future of having to get these things to work properly.
Given amount of duplication and variation there is no way to guarantee any level of security in such codebase. The bright future ahead!
The 35k lines of code is what made me think this was either a joke or the state of vibe coding, but no it turns out they're serious

I have entire codebases of embedded software in C without the shortcuts of modern programming languages in way fewer than 35k lines

I cannot find a link to the source code. Is there any?
https://www.recipeninja.ai/assets/index-B7TAGfYd.js

Not 35k lines, but there's one line that's about 48,000 characters long. So either they've intentionally obfuscated it, or chatgpt just churned out one long line

Edit: Running it through a prettifier the code comes about 33k lines

Oh, this. I thought there was more into it.

Edit: Run it through another prettifier, might turn into 35k lines. :D

It’s obviously obstructed, but throw it into an LLM to attempt to clean it up
(comment deleted)
Yeah 35k of code is a red flag. There's no reason for this to be that large.
That is nice actually. Recipe sites are so annoying where I have to scroll for an hour to see the ingredients. Thanks!
I never wonder about downvotes (and we are not supposed to ask) but why? Downvoted for a compliment.
You probably got downvoted because you admitted your failure of using the WWW. The problem you describe only happens on "recipe" websites that are 99% advertising and written like a blog, with every other word set in bold to maximize the ad revenue. These websites should be avoided entirely. Proper recipe sites include en.wikibooks.org/wiki/Cookbook:Table_of_Contents, based.cooking and of course https://web.archive.org/web/20110323153937/http://www.ottavi...
I love how the top recipe shows a literal stuffed lamb, e.g. a plush toy. Lots of the generated images have an early deep dream vibe
Nice work! The voice control is actually really, really cool. I was able to say "show me a chicken tikka masala recipe" which got me 3 recipes, and then I said "show me the third one" and it opened nearly instantly. Pretty awesome. I want this for my entire computer!
Is this a serious thing? If so, I'd like to know where I can purchase the wasp meat required to make this recipe: https://www.recipeninja.ai/recipe/r_NYrbOKZEyqrp7r/wasp-meat...
This is the funnies thing I have read all week. :D
I also wrote a recipe simplifying website, so this is a fun project to see. Incredible work!

Probably the main value engineers have for a maintenance project is context. I wonder what happens when we fully cede context to the machines...

Today, I got a request at work for a feature ("let's offer coupons!") that I thought would take a week. That was until I found out that another engineer wrote most of the code last year, and it'd take him a day to dust off.

I'm totally onboard with, and grateful for, larger-scale experiments like this...thanks for putting the effort in. I wonder how well Cursor (or similar) would handle a situation in which large amounts of code are _almost_ being used. What if 3k LOC accidentally get duplicated? Can our automated systems understand that and fix it? Because if they can't, a human is going to spend a _long_ time trying to figure out what happened.

Over the next 18 months, I expect we'll hear a few stories of the LLM accidentally reimplementing an entire feature in a separate code path. It's a whole new class of bugs! :D

I have similar thoughts and I have come to conclusion that that's the beauty and the curse of this technology. If one relies too much on it - it's gonna be a curse. However, if technology is used with care - it's a beauty. Not only does it keep SWE jobs "secure" - it really helps a lot for those who know what they are doing.

I think in the end AI will be more advanced tool, but a tool nonetheless. Like methodologies and principles, good practises etc. - they only work if you use it with care and added thought and adaptation to your case. DRY it a great principle. But sometimes it's better if you repeat yourself. For one reason or another. And these are the the tradeoffs that human in the loop should be making imho.

> I have similar thoughts and I have come to conclusion that that's the beauty and the curse of this technology.

I agree. When I read these articles on vibe coding I can't help to think that these guys are basking in the glory of the impressive maze they built around themselves. Of course running these things in production and having them reach the state of legacy code is an entirely different thing. Building a maze is one thing, having to run around it is an entirely different challenge.

It's like one of those world expos: everything looks fantastic, but the moment the event ends everything just crumbles.

I can tell. Ingredients. 1 skillet.
I think the recipes on your site are AI-generated? If you're looking to generate a large number of recipes, I highly recommend my tool: https://github.com/Yiling-J/tablepilot. It's specifically designed for this kind of task. Don't forget to check out the examples folder, I use recipes as examples a lot.
Quick note: In my opinion, the ai-generated food pictures are deep in the uncanny valley. Unappetizingly so. The hollandaise sauce image is disturbing in a way that's hard to describe, which somehow makes it worse.
What is "vibecoded" supposed to mean?
It's a newish term that I have understood to mean coding by letting the AI do nearly all the work, just telling it what you want done in broad strokes. If the code returned seems to "vibe" with what you're trying to do, then accept it.

Contrasted to AI assisted coding, where you would give much more detailed prompts with technical specifications, and read over every line to make sure you understand it before accepting a response.

In theory, vibe coding can let someone with very limited technical expertise build complete apps, so understandably a lot of people are excited by it.

In practice, it doesn't seem like we're there yet. But each new step in AI development leads to people trying again, and it's hard to deny that the results are getting better. I think we're at the stage of where AI image generators were a few years ago. Very much in the uncanny valley.

<quote:esperent> In theory, vibe coding can let someone with very limited technical expertise build complete apps, so understandably a lot of people are excited by it. </quote:esperent>

Actually it is booming. In bsky, X and Linkedin, I see another recipe/todo/budget management/profit tracking/SaaS starter template/landing page/people-to-follow-directory etc. being pumped out every single day. Before GenAI, this would be more like one partial feature per user every month, now post GenAI, entire product in weeks or even hours.

I believe, the indie entrepreneurs are making the maximum bang for the bucks with AI codegen compared to any other groups.

"Hey AI build me a node app that does X" "Now add a page for this" "Change this field to be that" "Integrate it with service X, Y, Z"
"don't forget to make it secure"
[flagged]
I clicked that link for you so you don't have to:

"There’s a new kind of coding I call “vibe coding”, where you fully give in to the vibes, embrace exponentials, and forget that the code even exists. It’s possible because the LLMs (e.g. Cursor Composer w Sonnet) are getting too good. Also I just talk to Composer with SuperWhisper so I barely even touch the keyboard."

Instead of complaining, why don't you click the link that was spoon fed directly to your cursor for the canonical answer within 3 seconds?
Because the effort needed to summarize the content of the links is barely more than posting them in the first place.

Who knows what kind of time wasting is on the other side of a link these days either.. Dark pattern cookie pop-ups, subscription pop-ups, intrusive ads, account registration demands, pay walls, etc..

Take a risk. Following links is better for the health of the web.
Because it allows readers to get the information straight from the source, instead of relying on some random unsourced comment that's probably going to mangle the definition of a term as nebulous as "vibe coding".
Because the whole point of writing and publishing things online is that you don't have to type out the same explanations time and time again.
It is so hard to reconcile my view of Karpathy as a world class instructor who does great things like making you work out gradients via the chain rule even though you don't need to anymore with this version of Karpathy.
They're the same person. He's just a techno-optimist and interested in teaching.
Thanks for the reply.

But I really detest the ever-growing universe of unnecessarily obscure slang. It's not clever and not informative. Good slang is akin to a word that you can figure out from its Latin origins; it makes sense, might be clever, and you can figure it out if you think about it.

This is just douchey.

It's a term to describe using an agentic LLM based assistant to write the majority of the code but without even taking the bare minimum of time to review the output. I laughed when people naively claimed that it was intended for proof-of-concepts only and would never be used in actual "production".
the microphone symbol should be animated while listening.
How does one learn to vibe code
(comment deleted)
By practice. It requires some skill too, to constantly remove slop and keep the project from deteriorating. Usually by suggesting good architecture, asking for tests, and rewriting / cleaning up some bad code endlessly. Still much faster than manual development.
But how exactly !
1. Download Cursor

2. Tell it to make you something

3. Get frustrated when it doesn't work

4. Think about how to revise your prompt

5. Repeat from step #2

I prefer Aider, but that's a matter of taste.
Any free versions of Cursor or Aider?
Aider is free and open source, Cursor is free. You can connect Aider to various free models on OpenRouter, some of them state of the art.
(comment deleted)
(comment deleted)
I have a paid subscription to Windsurf using Claude. I struggle to find much utility in terms of actually writing code. It spends a lot of time trying / retrying and glitching out. I'm sure in a couple of years it will be amazing (assuming a fundamental breakthrough isn't required) but it isn't quite there yet.

LLMs are super useful but currently, the primary use case is teaching, not doing. For this reason, I think ChatGPT is really just as good as an AI enabled editor (or both if you don't mind paying for two subscriptions).

To correctly vibe code something useful, I find that I need to religiously give faith to the tool and forget all about SWE principles and best practices and instead treat it like a child who makes mistakes and corrections while the adult must not intervene or admonish too much but rather nudge it to right direction.

Also vibe code has a parallel feature, while the code is generating, you are also doing live review and correcting it towards right direction, so depending on your experience, the end product can be a bad mess or wonderful piece of creation and maintenance dream.

The issue with seasoned SWE is that, the moment a mistake(or bad pattern) is made, the baby is thrown with bath water.

For a tiered app like the one presented, 35k LOC is not really that impressive if you think about it. A generic react based front end will easily need a large number of LOC due to modular principle of components, various amounts of hooks and tests(nearly makes us 25-40% of LOC). A business layer will also have many layers of abstractions and numerous impl. to move data between layers.

The vibe code shines, when you let it build one block at a time, limit the scope well and focus. Also, 2-3 weeks is a lot of time to write 35k LOC. at start of any new project, LOC generation rate is very high. But in maintenance phase it significantly falls as smaller changes are more common.

My experience matches yours.

Lots of apps are quite repetitive: for building APIs for example you generate one controller and the ask the app to generate more using the first ones as a pattern. For frontend you do the same for forms or lists.

Tests are often quite good, but I think they were already great even back in the first ChatGPT release.

With this strategy and the fact that some patterns are quite verbose (albeit understandable for an AI or a reader), it is quite easy to get to a big LoC while still maintaining consistency.

I've had a bit more success on the front end as it is possible to see the results of a change very quickly. In fact I would prefer it to just auto-apply the changes and I would visually inspect. It isn't bad but the workflow is pretty slow. The resultant code is also very verbose - likely 3X more code than an experienced engineer would create (this is one part that I am certain will improve dramatically in time). While I do use this workflow, sometimes I feel like I am just being lazy as opposed to productive.

I'm just being honest. For my use case, I would be much better off if LLMs could just do everything.

Full Disclosure: I'm building a LLM chat app for software developers and domain experts

> I struggle to find much utility in terms of actually writing code.

I personally feel you need to give up some control and just let the LLM do its thing if you want to use it to help you build. It honestly does a lot of things in a more verbose way and I've come to the conclusion that it is an LLM writing code for another LLM. As long as I can debug it, I'm okay with the code, as I can develop at a pace that is truly unreal.

I finished my "Recent" contexts feature in a half a day, today. Without the LLM, this would have taken me a week I think. I would say 98% of my code in the past few months has been AI generated. You can see a real life work flow here:

https://app.gitsense.com/?chat=eece40e2-6064-46d2-9bf1-d868c...

I truly believe if you provide a LLM with the right context, it can meet your functional specs 90% of the time. Note the emphasis on functional and not necessary style. And if *YOU* architecture your code properly, it should be 100% maintainable.

I do want to make it clear that what I am doing right now is not novel, but I believe most problems are not. If the problem is not well understood, it can be a challenge like my my chat bridge feature. This feature allows you import Git repos for chatting but I will probably need to rewrite 50% of the LLM code since the solution it built is not scalable.

I recently asked Claude 3.7 (Thinking) to help me debug. I told it that it seemed my code crashed when MY_FLAG was true. Its solution? if MY_FLAG: MY_FLAG = False

Do you come across issues like this too or am I not prompting it correctly?

I have very similar experience.

After seeing how people like Andrej Karparthy used vibe coding to generate applications https://x.com/karpathy/status/1903671737780498883?s=61 I realize that

you need to be clear on what you want the LLM to do break down the tasks and give byte sized tasks to llm to do specific thing and sometimes I had to tell it not go and change random files because it found the need to refactor them.

> LLMs are super useful but currently, the primary use case is teaching, not doing.

For code? Autocomplete on steroids is the killer-app.

The other things the LLMs give me are prone to be over-engineered/overly verbose code or similar.

I went through a lot of "Why are you also doing $FOO then $BAR? Doesn't seem necessary if we skip them and do $BAZ which will make one or both of those redundant" and it responding "You're right! Lets use $BAZ instead".

And giving them code to make a small change to was pointless - they would often, but not always, make an incidental change far from the point where you asked for the change.

But autocomplete? That works just great and because I've already got context of the code I am writing I can check it in (at most) two seconds and move on.

I think you're missing the value of rubber-ducking with the chatbot on a fairly well-defined/well-known problem in a somewhat specific/novel scenario—this is the vast majority of software development being done today.

Depending on the situation this can be invaluable. If you're experienced in the domain you probably know generally what you need to do but you might get a better result by reasoning through the best solution with the constraints and requirements you have. Or maybe you missed something obvious when you write out the full context—which is a required step for getting a good output from the chatbot, and generally isn't a required step if you aren't explaining your approach to someone else and you don't want to be rigorous.

> I think you're missing the value of rubber-ducking with the chatbot on a fairly well-defined/well-known problem in a somewhat specific/novel scenario—this is the vast majority of software development being done today.

I actually do use ChatGPT for rubber-ducking, but in that context there is no (or very little) code. In a coding context, I've resigned myself to purely autocomplete-on-steroids.

The thing is, in the vibe-coding context (having the LLM write the code for you), I've had atrocious results across all of the popular LLMs.

This does seem like something that is worth getting to the bottom of. Why are some people having such good results while others do not. What is the secret sauce?