AI tools for developers: principles, insights, and the importance of clean code.
Every day, I encounter a new AI tool, whether a new capability is added to an existing tool or an entirely new offering. I actively use several AI tools, including ChatGPT, Grammarly, and Amazon Q Developer, each serving different purposes. I pay for pro access to ChatGPT and Grammarly, though I sometimes consider switching to Claude.ai, which occasionally proves invaluable. s a view of AI tools for developers: my principles, insights and ultimately the realisation that development practises such as clean code is more essential than ever
Skipping the Hype Cycle
If you’ve read some of my other posts, you’ve likely seen me wrestle with AI coding tools in general and specific ones. In hindsight, I created many of these challenges for myself by buying into the hype. I hoped an AI could write a perfect application using a short description. I didn’t want an AI to replace me, but I hoped it could handle the boring bits effortlessly and flawlessly.
Reality shattered these hopes on the “trough of disillusionment”. My daily experience using AI for non-coding tasks took me to the peak of inflated expectations and beyond. Despite knowing that my AI usage in other areas showed limitations, I convinced myself I could make AI generate the perfect code. Hype can be powerful!
Slope of Enlightenment
After the bubble burst, I reflected on my mistakes and decided to try again. I began a new article series to detail how I productively and effectively use AI as a coding assistant. Before starting that project, I redefined my relationship with AI tools and reconsidered how and when to use them.
In this post, I share the principles I now follow when using AI as a development tool. I’ll apply these principles to my new project and describe my journey in future articles.
Practical Principles
To be clear, these principles are ways of working with AI. I’m ignoring any of the broader ethical considerations around using AI for creation. Hopefully, it’s clear that the code in italics is the principle; the rest is commentary.
Principle 1 – AIs Don’t Reason About Code
AI isn’t intelligent. Most code-assist tools rely on mainstream LLMs. Even if trained on “code,” they don’t reason about programming. They recognize patterns in text, not logical structures. LLMs only mimic reasoning: LLMs like ChatGPT are pattern-matching systems trained on vast amounts of text, including code. They do not reason in the human sense.
AIs produce reasoning-like outputs by correlating patterns in the data—always remember that these tools lack true comprehension. If the most common source of an answer to your question comes from StackOverflow data, then that’s what you’ll get. You don’t have an expert at your call. You just have the internet via a smart agent.
Principle 2 – The AI Is Only as Good as Its Training and Data
LLMs struggle with complex tasks requiring multi-step logical deductions or nuanced programming contexts. They can quickly fail at subtle nuances in programming logic or context-sensitive requirements not sufficiently described during training.
AI can fail when creating applications with poorly documented third-party APIs or insufficient code samples. Often, AIs lack training on the latest API changes, so targeting older API versions typically works better.
Principle 3 – Be precise and focused
Provide detailed, focused prompts. Aim for clear, concise requirements. For example, ask the AI to generate one functioning class rather than multiple classes. Use the AI to create skeleton setups with comments rather than asking for too much code at once. You’ll save time and effort by working within the AI’s capabilities.
Asking an AI to generate more than a few 10s of lines of code will require rework and often lead to significant wasted effort. Work with the AI and within its capabilities, not to your aspirations.
Principle 4 – Plan to Iterate
Expect to revisit and refine AI-generated outputs. AI often hallucinates plausible but incorrect code. Reworking prompts can help, but you’ll likely need to debug interactively. Treat the AI as a collaborator rather than expecting perfect results on the first attempt.
Returning to the AI with error messages can be frustrating when the AI apologises or tells you the code it generated is wrong, but it’s still more productive than trying to generate a lot of code all at once. Expect to discuss the issue continuously with the AI as you tease out bugs.
Principle 5 – Commit Often and Regenerate When Needed
AI-generated code can frustrate when it introduces issues or fails to align with requirements. Regularly commit changes so you can revert and explore alternative approaches. Be prepared to manually correct errors while rescuing useful components of the AI’s output.
At some point, the generated code will often be frustratingly wrong. Something will be off. You might want to change a parameter type or add logging. Whatever you want fixed will be a challenge for the AI to correct. It will get the wrong idea, say it’s made a change and hasn’t, or, even more frustratingly, make the change and break something else. Be prepared to do some fix-up as you rescue value from the codebase by going to previous versions and taking a different tack.
Principle 6 – Teach the AI
When AI loses track of its task, ask it to generate a prompt to restart the session. This often refreshes its memory and provides a foundation for continuity. Additionally, share your codebase and engage the AI by asking it to explain the code. Correct its misunderstandings to refine its outputs.
When generating code, documents, test cases, etc., you often reach a tipping point when the AI begins to forget what it’s supposed to do. Keeping the AI on task requires continuously guiding it with feedback. Remember, the AI isn’t sentient or intelligent.
Principle 7 – Use AI to ITS FULLEST
Leverage AI for code reviews, education, and planning. Use it as a vast repository of common best practices but validate its responses with external references. Iterate with the AI to refine your approach and identify potential challenges before coding.
You can and should use AI to help with all parts of development and testing. AIs are good as code reviewers and capable of providing concise education. The right AI can help you avoid pitfalls before you start coding. Use it as an inexhaustible expert on what the world thinks (on average) is the right way to do things. Be aware of hallucinations and ask essential questions in different ways. Challenge the responses and ask for external references. Iterate with the AI on your plan. At some point, ask it to call out out any challenges it sees with your approach.
Principle 8 – Use Multiple and Specialized AIs
Compare outputs by using multiple AIs for the same task. For example, run the same prompt in ChatGPT and Claude.ai to gain different perspectives. Use IDE-integrated AIs, like IntelliJ AI Assistant or Amazon Q Developer, for in-context advice. Use general-purpose LLMs for broader guidance but treat code generators cautiously—they often overpromise and underdeliver.
There are two points here. Use multiple AIs of the same type to compare what you’re getting. That doesn’t mean everything, but it’s good to take code or prompts or AI pontifications and run them past another one. Got ChatGPT? Take the prompt and use it with Claude.ai or vice versa. You’ll get a different slant, but it’s a simple and powerful way to get validation.
The other point is to use the right AI in the right way. Most of the AI tools we use as developers are LLM-based and will hallucinate in annoying ways. Minimise the impact by ensuring it’s easier to spot the hallucination and that it’s presented in a way you can ignore. The AI coding assistants that are built into the IDE – such as IntelliJ AI Assitant or Amazon Q Developer are potent tools for developers because they have context (i.e. they can read your existing code) and have been trained to provide this in situ code advice and, most importantly – you can ignore it if it’s not right.
Use mainstream LLMs to provide guidance and education on all development matters, Use IDE auto-complete and code context-sensitive AIs to improve code in point ways (and definitely improve your productivity), and be careful about using code generators – whether from your prompt or in those over-the-top cases were you hear of an AI that can create a mobile app from a hand-drawn image. We’re not there yet. The risk is that you’ll spend more time trying to get precisely what you want when you could have just written the code yourself.
Principle 9 – Non-Functional Aspects Are Often Missing
AI often overlooks logging, security, performance, and other non-functional requirements. While you can add these elements later, prompting for them upfront can dilute the AI’s focus. Treat AI-generated suggestions as starting points for performance or security improvements, not final solutions.
Security, performance, observability, and even simple logging. So much of what else we code beyond functionality can be missing from what’s generated. Some items you can (and should) add in later.
It’s much easier to ask an AI to add logging, documentation, or exception handling to existing code than to generate it upfront. Including these sorts of elements in your prompt can often result in poorer code, as the AI is less focused on the important elements.
Performance is a thorny topic. It’s conceptually possible that an AI can improve the performance of existing code – especially at the function level. However, it’s hit and miss. Remember, AI can’t reason the way we do. They’re looking for a pattern match between human text and code samples. AIs can critique code for performance, but generating performant code is much more challenging. This is the same with security, so be careful there too.
Principle 10 – Clean CODE MATTERS
Validate AI-generated code by running static analysis tools, etc. Take time to validate that the code compiles and passes rigorous quality checks. Don’t shortcut unit and integration testing. Supplement these steps with thorough human code reviews, as AI mistakes can range from simple oversights to wildly inappropriate implementations. Invest in clean code practices to uncover and address AI-generated issues.
How do you validate that the code generated is good? Do you generate test cases, too, and assume that it’s okay for AI test cases to test AI code? Testing is certainly an important aspect, and it’s good to be clear to the AI that any code it generates should be done with an expectation for unit testing. Otherwise, you can get quite gnarly code that needs refactoring to pull out the testable methods.
Beyond that, you must invest in extra safety checks. For code that will be compiled, the first test is that it compiles! The next level is static analysis, linters etc. The tools we’ve relied on to find sharp edges and poor assumptions in our code will be invaluable with AI-generated code. AIs may not reason about code how we want them to, but code analysis tools do. Investing in good code analysis tools will pay dividends when using AI code generators. It’s going to be your last automatic safety net.
The final check is to increase (or introduce) human code reviews. The code the AI is generating is generally going to be average. Usually, the code will be straightforward to review. However, AI can make mistakes (sorry, hallucinate) in every way. The mistakes a reasonable developer will make are not the only sort of problems an AI will create for you. AIs are quite capable of forgetting variable names from one line to another, generating Javascript in the middle of Python, inventing methods to call on 3rd party APIs that don’t exist and using 3rd Party APIs completely the wrong way.
You must revisit your development practices when adopting AI code generators. Principles of clean code are significantly more important now, as this is your last chance to discover those pesky AI code challenges before you go into production.
Final comments
I expect I’ll rewrite these principles to make them more pithy. ChatGPT helped me extract some of the essence (and pointed out that I had missed referring to the ‘ethics’ question).
All technologies have a yin and yang. There’s always a downside to the up. What usually happens is that the upside gets more realistic, and the downside solidifies and reduces as we refine what the tech really is good for. We’re all still on the AI journey; we’ve barely taken any steps yet, but I still have high hopes for what we can achieve.
I’ve learnt a lot about how not to use AI for coding. Much like trying to use a chainsaw to cut butter, I’ve learned that using powerful tools in unconstrained ways is messy and unproductive. However, by engaging with AI and using it more as a pair programmer and less as a master Jedi, I’m finally getting value.