Over the past two weeks I’ve been deep into a vibe coding adventure, building a golf coaching app with Windsurf.

ChatGPT giving me my first prompt ChatGPT giving me my first prompt

I’ve used AI (mostly ChatGPT and Microsoft CoPilot) quite a bit for programming, but this was the first time I went all-in from inception to app delivery.

Building the developer spec

I pay for ChatGPT Plus at $20 / month so I started there. Following suggestions from Harper Reed I had ChatGPT (o4‑mini) interview me to build a developer specification. This is my prompt:

I’m going to build a new web app. Ask me one question at a time so we can develop a thorough, step-by-step spec for this idea. Each question should build on my previous answers, and our end goal is to have a detailed specification I can hand off to a developer. Let’s do this iteratively and dig into every relevant detail. Remember, only one question at a time.

Here are the first few questions I was asked:

  1. What’s the primary purpose or core functionality of your web app? (Describe the main problem it solves or the value it provides.)
  2. Who specifically is your target user? (e.g., beginners, intermediate players, advanced amateurs, professionals, coaches—please also include typical age range and level of tech-savviness if possible.)
  3. What specific problems or frustrations do these golfers currently experience with their practice routines that your app should aim to solve? (Consider issues like lack of structure, poor tracking, limited time efficiency, lack of personalization, difficulty measuring progress, etc.) … The final question: Would you like me to generate a written specification based on everything we’ve outlined so far, formatted and structured for developer handoff?

Why yes, I would love that! It produced a spec that I saved as a Markdown file as the first commit to my git repository.

Building the app blueprint

Next up was to have the same AI translate that spec into a plan, which I did with this prompt:

Below this paragraph I’m going to share a developer spec for an app I’d like to build. Draft a detailed, step-by-step blueprint for building this project. Then, once you have a solid plan, break it down into small, iterative chunks that build on each other. Look at these chunks and then go another round to break it into small steps. Review the results and make sure that the steps are small enough to be implemented safely with strong testing, but big enough to move the project forward. Iterate until you feel that the steps are right sized for this project.

The output from this prompt was impressive: a detailed iteration plan spanning 9 iterations. But why not go further? I then issued this prompt:

From here you should have the foundation to provide a series of prompts for a code-generation LLM that will implement each step in a test-driven manner. Prioritize best practices, incremental progress, and early testing, ensuring no big jumps in complexity at any stage. Make sure that each prompt builds on the previous prompts, and ends with wiring things together. There should be no hanging or orphaned code that isn’t integrated into a previous step. Make sure and separate each prompt section. Use markdown. Each prompt should be tagged as text using code tags. The goal is to output prompts, but context, etc is important as well.

This resulted in 9 different prompts that I would then use to build the app. Because I knew I wouldn’t do this all in one session, I also had ChatGPT create a todo list:

Can you make a ‘todo.md’ that I can use as a checklist? Be thorough.

And yes, I would just have my programming bot check things off the checklist; this todo.md file also went into my repo.

Building the app with Windsurf

Golf coaching app dashboard Golf coaching app dashboard

I considered both Windsurf and Cursor but I lucked into some fortunate timing with Windsurf: they were in the middle of a pricing model change and for about 2 weeks they offered ChatGPT-4.1 for free with just their trial account. This meant I didn’t have to frugal about my prompting. I had Windsurf do everything, only rarely intervening. There were a few challenges:

  • It had a hard time working with the Tailwind CSS library, largely because it was applying 3.x conventions to 4.x. Sometimes just reminding it “hey, remember you are working with Tailwind 4!” would work; other times I had to manually fix things.
  • Windsurf seems unable to remember little things like that I’m using a virtual environment for Python. I even wrote helper scripts to run the app and to run the pytest suite, and it would never remember to use those. It kept trying to run Flask outside of the venv. This might be operator error, as I think I could have configured the environment (this is just a repurposed VS Code after all) and maybe it would have worked?
  • I found that incremental UI/UX work is better. Giving a prompt like “hey make this app fully responsive” produced an unrecoverable mess. git restore . and start all over again with smaller changes, e.g.: make the drill boxes turn into a single column when the browser window gets too small.

Detailed drill examples Detailed drill examples

One surprisingly cool thing: I needed to populate my drill library (which is configured as a set of Markdown files that get imported to the database) and a simple prompt of “go out and fetch 5 or so drills from each of the strokes gained categories and save them in the proper markdown format” just worked. I didn’t love the drills it found, but they gave me working content I could use to test the app.

Conclusions

I love this approach to get started on an app I want to groom and turn into something real. It would also work for a more dispensable app that is more throwaway, though I tend to just use CoPilot directly for something like that. Or have ChatGPT generate a single-file Python or bash script.

The code quality was pretty decent, and I even learned some things by reviewing its code. Silly me, I didn’t know you could have SQLite use an in-memory database, hugely helpful for testing:

@pytest.fixture
def app():
    app = create_app()
    app.config['TESTING'] = True
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    with app.app_context():
        _db.create_all()
        yield app

My son Jacob has some different thoughts on using AI for development, and I think for his context (working on long-lived enterprise software with a larger team) it is a good approach. His suggestion is aligned with my approach here, up to the point of using a tool like Windsurf or Cursor to “just do it”.

More to come on this as I have some other experiments in the queue.

Updated: