Sitemap
Nolan Wolff

Nolan Wolff

Delivering C-level leadership, product management expertise, and corporate strategy advice. Photographer. 63 countries so far…

The nine steps to successful coding with AI tools

8 min readJun 19, 2025
Press enter or click to view image in full size
Image generated by ChatGPT

I recently launched Hack Your Loan (www.hackyourloan.com), a SaaS fintech that helps home buyers in Australia maximise their interest savings by optimising their home loan structure.

I wrote “How I built a fully-featured SaaS Fintech with zero coding experience, for less than $100”. You can review Part 1, Part 2 and Part 3 for context but it’s not required reading for this article.

My experience working with AI tools to build Hack Your Loan was both exhilarating and terrifying at the same time. I experimented with a range of AIs and learnt the hard way about what did — and didn’t — actually work.

I wanted to share some of those insights and have distilled those down into nine practical steps.

  1. Start with a plan

Although coding with AI tools feels magical, it can’t read your mind (yet). Starting with a plan meant I could both (a) clarify things in my own mind, and (b) ensure that the AI had the context of what I was trying to build. The latter is absolutely critical — see my point about “Context is key”.

For more complex projects, a plan also allows you to figure out how things fit together, and which pieces you should build first.

For example, at the very start of the project I outlined my thinking on a range of topics, including Purpose and goals, Design preferences, Frameworks and libraries, Pages and structure, Key functionality, and Security concerns.

I then shared these ideas with Cursor, and had a variety of “brainstorming” discussions on updating and improving my approach.

I used this approach multiple times over the course of the app’s development. For example, whenever I was ready to work on the next major feature (e.g. incorporating Cognito user identity management, or integrating with Stripe payments), I would have a detailed and wide-ranging discussion with ChatGPT on the topic.

Even if you don’t know what questions to ask, you can generally get the AI to help you with those too: “If you were building this app, what are the most important pieces of functionality that you would consider?” “What are the downsides to the approach you just suggested, compared to x and y?” Then iterate and drill-down on the responses until you feel comfortable you understand the options.

Once I had a high-level plan, I would ask Cursor to develop an implementation plan based on those requirements. For example, here’s the prompt I used when integrating Cognito for user identity management:

We’re now going to embark on a major update to the project, where we will integrate Cognito and use JWTs to control access to certain components on the webpages, and also ensure that only users with certain privileges can access APIs.

I have created an overview document with my initial thoughts and some high-level requirements: @[file name.md]. Please review this document.

I will need a detailed implementation plan, followed by step-by-step instructions for each part. I’ve created a blank markdown document [file name.md] to get you started.

Let me know what you think and then ask any clarifying questions.

Two things to note:

  • I frequently used implementation plans to help me understand the scope and complexity of the required changes, and (perhaps more importantly) I could track my progress
  • The request to “ask any clarifying questions” was absolutely essential. Cursor was able to validate that I was on the right track (or not), identified any areas where I hadn’t been entirely clear on my requirements, suggested alternatives when asked, and also spotted use-cases that I hadn’t considered. Being able to improve my requirements before writing code was powerful.

Remember that you still get to make the decisions, not the AI. And my strong suggestion is for you to own those decisions because you’re going to have to deal with the consequences, not the AI.

2. Work the hard problems first

This is a personal philosophy and was central to guiding my thinking from the very start. It helped me ruthlessly prioritise which features to focus on.

Here’s an example of my thought process:

“Don’t worry about anything before you’ve shown you can convert the Excel logic into code”

“Can you find a way to make the optimisation algorithm available to others without giving away the ‘secret sauce’?”

“Can you set up AWS and build a secure API?”

“Now that you have the basic app in place, can you integrate user identity management?”

“User identity management is working. Let’s try integrating payments”

I knew that if I couldn’t deliver the basics, there was no point in thinking about more advanced functionality. By starting with the riskiest elements, I de-risked later stages of development and increased the chances of everything else working smoothly.

Get Nolan Wolff’s stories in your inbox

Join Medium for free to get updates from this writer.

3. Context is key

I learned the hard way that context is the best approach to getting quality output from the AI. The more specific you are about what you want (and what you don’t want), the more likely you are to get the output you want. Vague prompts inevitably result in vague output.

4. Small changes are best

I do not recommend allowing the AI to make wholesale changes to your code. Yes, you might occasionally win the lottery and have a single prompt result in a massive chunk of functional code. However, my experience is that — like winning the lottery — this doesn’t happen very often, and you’re more likely to spend a ton of time trying to untangle and debug that code. In addition, even if the code is functional, you’ll still need to iterate multiple times to deliver the results you want.

Instead, I suggest focusing on making small changes with limited scope. It gives you time to test and to make backups of code that you know is working (see points below). This feels slow in the short-term but likely to save a bunch of time and frustration later.

I should note that there are instances where Cursor outperformed with large scale changes. In particular, when cascading updates across multiple files, Cursor was able to do so quickly and consistently. Its “autocomplete” tab function was also impressively accurate at predicting both changes to code and content.

5. “Accept all” is a bad idea

It’s useful to remember that all LLMs (code-centric ones included) are ultimately statistical models at their core, and are all subject to giving misleading information and hallucinations. For example, Cursor kept “helping” by rewriting huge chunks of code, far beyond the scope of what I had asked.

So if the AI’s suggested changes don’t look right, don’t be afraid to reject those and ask to start again — the AI isn’t going to take it personally. I rejected the AI’s updates almost as often as I accepted them. Ask the AI to explain why certain code has been changed, and ensure that the AI has the correct “understanding” of what you want to achieve.

That said, the AI generally won’t get complex changes right on the first attempt, and you may need to “see where things go” before reverting recent changes. Knowing where to put a stop to that process is more of an art than science but as long as your backup strategy is in place, you should feel safe to explore alternatives.

6. Bug fixing

While the AIs occasionally got stuck on critical bugs (see Part 1), I was pleasantly surprised how efficient Cursor was at fixing bugs.

The approach that worked best was sharing as much context as I could, including browser console logs, CloudWatch logs, and screenshots (Cursor was surprisingly good at interpreting images), along with a detailed description of what had happened. I simply copied and pasted those into the prompt and let Cursor review and interpret. Cursor was frequently able to identify and resolve the issue immediately, or within one or two iterations.

Of course, don’t forget to add explicit logs into your code. I found the AI very good at suggesting suitable things to track.

7. Plan for a multi-environment deployment

I knew from the start that I didn’t just want to cobble a demo together; I wanted to build a maintainable, professional-grade application. That meant I needed different “environments” which would give me a safe place to test new (and likely broken) ideas without breaking things for my live users. You don’t have to do this, but I strongly recommend it. It isn’t as hard as it sounds although it’s easier if you incorporate this approach into your plans from the start.

I used a three-stage setup (development, staging, and production), and the key was using “environment variables”. These are effectively a central location for storing critical URLs and other keys. I also set up my code to detect which environment it was in and automatically choose the relevant variables, reducing the likelihood of human error.

This planning was a lifesaver when dealing with the inevitable pain of CORS (Cross-Origin Resource Sharing) issues. Trust me, when you’re wrestling with a frustrating CORS bug, having all your critical URLs defined in one central, easy-to-manage place will save your hours of frustration.

8. Test early and test often

Testing might seem boring. However, not only does it result in better quality code, but I also found it was a sneaky way to keep my momentum and my motivation going.

I love working on UI: it’s great to see the impact of your changes immediately. Not so much with backend code. When developing APIs, for example, it might be multiple days before I integrated those changes into the frontend. Having Cursor develop a test plan and delivering successful test results from those backend functions (using AWS Console and Postman) felt like a genuine victory.

Also, I guarantee that tracking down a bug in a newly-created lambda beats trying to resolve a critical error among a maze of code once that lambda is part of multiple interconnected modules.

9. Make backups

I cannot emphasise this enough: Make backups. Frequently.

At some point, the AI is going to mess up your code so spectacularly that starting from scratch is the only option. And if you don’t have a recent known working version that might be a gut-wrenching decision.

I taught myself how to use the basics of GitHub (ChatGPT developed a tutorial for me) and it was well worth the effort.

On at least two occasions, I had to roll back long sequences of changes and would have been in deep trouble without a known working version. GitHub also integrates seamlessly into Amplify which meant I could deploy easily into a multi-stage environment.

Having those backups was also a huge benefit psychologically: I felt like I had a net while performing all those acrobatic manoevers.

In conclusion, building an application from scratch has been one of the most challenging and rewarding things I’ve ever done. These nine steps aren’t a magic formula, but they are the hard-won lessons that kept me sane and productive while navigating the unpredictable world of AI-assisted development.

The tools are improving at a rapid pace. But the human element — your vision, your judgment, and your persistence — will always be the most critical component. So start with a plan and go build something amazing. You might just surprise yourself.

--

--

Nolan Wolff
Nolan Wolff

Written by Nolan Wolff

Delivering C-level leadership, product management expertise, and corporate strategy advice. Photographer. 63 countries so far…