No, the title is not ironic, there are some things I genuinely like about AI.
Many of my previous blog posts have focused on the problems with modern AI, in particular, the class of deep-learning algorithms known as "Large Language Models," or LLMs, and people who have read my blog have got the incorrect impression that I hate AI. So I would like to make it a matter of public record: though I am highly critical of this latest AI craze, I do not hate AI. My screen name is HAL9001 for a reason. Ever since I first saw 2001: A Space Odyssey, I have been fascinated with the idea of intelligence, and building computers that can think and talk. And I do always love learning new computing techniques and algorithms, and have been learning them my whole life. AI is indeed fascinating to me.
So in this article, I want to talk about my latest experiments with AI, especially using a service called "Claude Code" as a coding assistant, and the positive impressions that I have of it.
I do understand that there are people who are morally opposed to using AI, and I sympathize and even partially agree with those moral objections. I have talked about these moral issues as well, at length, in other articles on this blog:
So if you could indulge me, let's assume the problems of mass surveillance, fascism, racism, poverty, war, and climate change could all be solved tomorrow, and AI were no longer exacerbating these problems, would there then be a responsible and effective way to use AI as a tool in your professional work? I think there is a way, and I'd like to tell you now about my experience using AI in a professional setting.
I recently had an opportunity to work professionally on a web application using Claude Code. I had used TypeScript and React Router before at times throughout my career, but not enough to really be a fast or proficient software developer using these tools. Both TypeScript and React Router are extremely popular web development tools, if not the most popular, and there is lots of training data online to train an LLM in how to write software using these tools, so there was little doubt that the results created by Claude Code would be quite good.
After using it for two months (I have been using the Opus 4.6 model), I have a pretty good feel of the model's capabilities and quirks. So for the rest of this article, I'll explain the various things I tried, and how I eventually figured out how to fit Claude Code into my daily software development workflow.
I should also note, I switched to ChatGPT Codex not too long ago, so I no longer use Claude Code. But when I first started writing this article, I was mostly using Claude Code, so this is what I talk about most. Everything I say about Claude Code applies equally well to ChatGPT Codex.
(Honestly, I cannot even tell any difference at all between Claude and Codex, they both function equally well.)
TL;DR: I think AI coding assistants really do make me more productive
With some caveats, I would say using an LLM coding assistant has definitely made me a much more productive software developer.
AI coders are pretty good linters.
A linter is a program that checks for basic errors in your code. "Mechanical" linters (as opposed to LLM linting), tools such as like ESLint or PyRight, are still far more accurate and less expensive. Mechanical linting is really fast and runs for free, without consuming any expensive tokens at all, so you should still use mechanical linters. Even better is to use a strict statically typed language like Haskell whenever possible, the Haskell type checker is the fastest, most accurate, least expensive way to check your software, so long as your software is written in Haskell. But if you can't use Haskell, asking Claude Code to do a code review of your code often uncovers mistakes that the mechanical linter or type checker misses, for example, whether your code do what your comments claim it does. Asking Claude Code to review code has been a huge time saver for me.
AI coders are the ultimate rubber duck
Claude Code is very helpful at getting me unstuck when I am stuck on a problem. There is a technique software engineers use called "rubber-duck debugging." When you are trying to fix a bug that is difficult to solve, explain the problem to an inanimate object. The act of explaining by speaking out loud is what helps you solve the problem. Claude Code takes that a step further. I explain my problem to Claude Code, and it replies to me with the tidbits of knowledge it has collected from countless other people who have had and solved my problem before. I now find I can go all day writing code without ever getting stuck on a problem. The LLM coding assistant keeps me moving along, it keeps me focused.
AI coders are the ultimate template generator.
For many years prior to the invention of LLMs, people were complaining about how horrible the state of web app development was, and a large portion of these complaints were related to how much boilerplate code was required to accomplish the simplest tasks. One tool we use to mitigate the boilerplate code problem has always been to make use of project templates. Ruby-on-Rails is especially good at this. But now, Claude Code does this job extremely well. It goes beyond mere template generation, it can actually start generating stub functions, and setup a basic test suite for you. This is a tremendous time saver.
Beware of the limitations of AI coding assistants
As long as you keep in mind these limitations, you can avoid the pitfalls of using AI coding assistants, rather than waste time fixing any mistakes it introduces into your code.
AI coding assistants are getting to be very expensive. It is not a good idea to give it an arbitrary task and just let it try to figure out how to solve the task on it's own, this will waste expensive tokens and produce results that are at best, poor sloppy code, and at worst completely useless. Meet the AI assistant half way. Use the software development tools you have always used and trusted to solve the problem. Once you have an idea of how to solve the problem, explain it to the AI, and it can write the code for the solution for you. Not all of the time, but most of the time, this is faster than writing the code for the solution yourself.
Don't be afraid to spend time writing the important code yourself, then let the AI coding assistant find your mistakes. This save me from so much frustration and exhausting and tedious debugging.
An AI coding assistant can write the unimportant code for you quickly and effectively. Some examples are, boilerplate code for large data structures, data structures that match an SQL query, writing-up building, testing, and installation scripts, writing "sanity check" test cases (but you should write your own test cases for the more complex logic), and fine-tuning the graphics code and CSS code for web applications. An AI coding assistant can save you the trouble of writing all of these things by yourself, which is nice if you would rather be solving more interesting and important problems.
If you are not careful, you will be tempted to let the coding assistant write too much code for you. At first I though reading the code would be enough to make me understand the software I was working on. But reading code is not enough. You have to write it, and you have to be responsible for it's correct functioning. If you are not doing that, you will not learn about the problem you are solving, you won't be able to explain your work to others, you will start to forget how to write code and solve problems for yourself. Do not fall into this trap, do not trust the "vibes."
Slow down. Never write software, or prose or verse for that matter, faster than you are able to think about it yourself. "Good writing comes from good thinking," as my school teachers always used to tell me. Always remember to stop and really think about what you are doing. And don't pressure other people to work at your pace either. Your job is not to write software, it is to understand how software systems work and take responsibility for their correct functioning. That understanding takes time, you can't pawn that responsibility off onto an unthinking machine. If you were hired as a software engineer, understanding is what you are paid to do, so take the all time you need, no more time and no less time than that.
What LLM coding assistants do well
AI coding assistants are pretty good linter
This means AI coding assistants are pretty good at catching your mistakes. This is by far the most important benefit of a good coding assistant.
Sometimes, simple errors can really throw me off, especially when I am tired or frustrated. The reason I love Haskell so much is that it was the first language I had ever used where the compiler used a rigorous form of mathematics to catch all the mistakes I made when coding. It was a profound, almost religious experience for me when I realized if you just change the language you use, you can become a better, faster, more productive software engineer. Just let the computer catch your mistakes, instead of you trying to catch your own mistakes.
As a result, I came to truly hate, with a passion, languages that I was forced to use for work which did not have strong or static type checking — especially JavaScript and Python, the worlds two most popular dynamically typed programming languages. I am forced to use, JavaScript and Python constantly even though I know for a fact that these languages are objectively not very good languages at all. But this isn't a problem anymore, because LLMs can paper over the deficiencies in these languages and provide a kind of checking on your work that is in many ways nearly as good as the Haskell type checker.
True, LLMs are far from perfect. For one thing, LLMs are considerably more expensive and energy intensive than Haskell's type checker, which is free and runs quickly on any small computer. LLMs are also no where near as rigorous as Haskell. Haskell's type checker is a theorem prover that can catch all type-theoretic inconsistencies in your code, whereas LLMs can only guess. But there is just not enough wisdom or justice in this world to make a majority of software developers switch to using Haskell, so the expensive solution, LLMs, is what everyone will have to use. Shout-out to the Rust language is a pretty good middle ground, and it is very popular.
LLMs, for all their flaws, have mitigated (not solved) the problem of dynamically typed programming languages being the worlds most popular languages. So pragmatically, now that the type checking problem has been mitigated for Python and JavaScript, I have discovered that for the first time ever in my life, I am actually having fun writing Python and TypeScript in my day job. And this is no small improvement to the quality of my life.
Claude Code Opus (version 5.4 at the time of this writing) is good at automatically solving simple errors. I can give it a stack trace, it does a code review of the files and function call sites listed in the stack trace, and can usually find the bug pretty quickly. When it cannot find the bug, when the error is not so simple, I can start investigating the issue on my own, and I can accept that as an unavoidable part of my job. But to have simple bugs solved for me so quickly when it could take me hours to figure out which tiny detail I overlooked, this is the most helpful thing about Claude Code.
Debugging is exhausting, time consuming work, and to have a coding assistant that can solve a larger number of these problems for you doesn't just save a lot of time, it eases the frustration of one of the worst part of my job, and this is a huge improvement to my own quality of life. Although it is true that using an LLM to do work does tend to burn people out, I think this has more to do with the cost of tokens and the rate limits, which encourages people to hyper-focus on tasks until reach their token limit. But if that restriction were lifted, if AI becomes cheaper to use, I think AI could begin to help people feel less burned-out and less frustrated with their jobs by relieving people of the tedium of searching for needle-like problems in haystack-like piles of computer code. Though right now, AI coding assistants are having the exact opposite effect.
Furthermore, because AI coding assistants are good at debugging, it is also good at finding security holes in your software. It has been shown now that LLMs are exceptionally good and finding bugs that can be exploited by black-hat hackers, and likewise it is helping white-hack hackers find these bugs and prevent them from ever being used by the black-hats to cause real damage. In that sense, LLMs can actually help to keep people safe.
In an more idyllic world not crumbling under the rot of corrupt governments and what the kids nowadays like to call "late-stage capitalism," AI coding assistants could definitely make the lives of software developers so much better.
The ultimate rubber-duck
Rubber-duck programming is a trick programmers have often used. When you get stuck on a problem, explain the problem to an inanimate object (e.g. a rubber duck). It is the act of explaining the problem that helps you exercise your reasoning faculties, and this helps you find the solution to your problem. AI coding assistants take this a step further. As you explain your problem, your problem becomes a prompt. So you not only understand the problem better, you can use it as a starting point for the LLM to lookup a solution for your problem. The AI coding assistant can even fill-in the blanks of it's copy-pasted solutions with details specific to your own program.
And I should mention that writing code well requires being able to think well. LLMs do not think or reason, they are more like lookup tables, where you can search for code that solves a problem by explaining the problem in a "natural language" (such as English). A large number of computer problems have already been discovered, explained, and have a coding solution written for them on the Internet. These solutions have been compressed into an immense statistical data structure called a "language model," and are available for you to lookup with a prompt written in natural language, and the result can be used to solve your own computer problems. But the models are not thinking.
So if you really want to do your job well, you have to understand the goal you want to solve, you have to understand the challenges you must face to achieve that goal, and you have to think about how you want to overcome those challenges. So can use the LLM to overcome those challenges, but do not use it to think for you. Unless you are thinking for yourself, you aren't really, truly solving a problem, you are likely only creating a new problem.
So do "rubber-duck" debugging with the LLM. Do it to help yourself understand the problem, then let the output of the LLM guide you toward a solution. In my experience, this is a very good way to get work done.
The ultimate template generator
An AI coding assistant can lookup and apply existing solutions to the most common coding problems, because the solutions to these problems have already been thought-about and written down. Once Claude Code recognizes the topology of the code you need to write, it can search it's vast "memory" for similarly shaped solutions, paste the solution, and fill-in the blanks with details specific to your computer program.
That said, I think it is a big problem that AI coding assistants prefer to copy-paste known solutions rather than reuse existing code. On the one hand, there are many times when copying and pasting code is the best solution. Obviously, when you first start writing your software, there will be a lot of boilerplate code and setup code you will have to write, and AI coding assistants can do this very well. But on the other hand, copying and pasting code to solve every problem becomes very expensive in terms of both memory consumption, and in terms of software maintenance (keep the code up-to-date).
Writing software is an NP-Complete problem. You may not be able to reduce the number of steps it takes to write your program, but you can make those steps go faster by using a lookup table. So again, thinking about AI coding assistants as a lookup table that contains millions of code examples that you can search for with natural language, you can reduce the time it takes to solve the NP-Complete problem of writing software, especially since the lookup table is optimized to contain solutions to a software engineer's most common problems.
And to be honest, I would estimate roughly eighty percent of all problems that I face in my day-to-day work are "common problems," with common solutions that can be easily looked up with a search engine or an AI coding assistant. I imagine this is probably true for most all other software engineers as well.
For example, one of the most tedious parts of my job was trying to tweak the code that draws graphics on the screen, the CSS code. Now, an AI coding assistant can do it for me. It is a task that never required very much deep thinking. I used to just make guesses as to what code to write and see if it generated the correct image, it took a while but eventually I would find the right incantation and the image I wanted would appear. I don't have to do this anymore, because AI is very good at this kind of wrote memorization and recall, and can produce graphics according to a description of what you want with incredible accuracy. For graphics and front-end programming, AI coding assistants have made it much, much easier for me to get good looking and useful features shipped much faster than ever before in my life. It can even take a photo or screen grab of a mock-up and turn it into code.
Caveats
There are some very big caveats to this claim that I am more productive with AI, first and foremost that I have had decades of professional experience as a software engineer, and I can spot the problems that come up when I use AI to augment my work. Even asking the AI assistant to check it's own code for mistakes has it's limitations, which I discuss in depth later.
Claude Code and ChatGPT Codex are very good tools, they are the first useful LLM coding assistants I have ever used. But that is not saying much, because every pretty much every other prior LLM was worse than useless as a coding assistant. That said, these AI coding assistants still have some severe limitations that only make it useful for a somewhat narrow range of tasks primarily in the domain of web app development where I have used them extensively.
I can tell you with some certainty that Claude Code is not good at designing databases at all. It can write SQL code without mistakes, it can catch mistakes in the SQL code I write, but it's architectural decisions often leave me rolling my eyes.
For all but the most basic web applications, Claude Code requires a lot of hand-holding. Do not prompt it and let it work all on it's own. You need to carefully inspect and test every change it makes. You have to go back and forth with it on architectural decisions, you have to catch it when it makes bad assumptions.
When I try to prompt it by analogy to something that is not in the domain of web apps, like "make it work like in the X-Y-Z feature of the Gnome desktop environment", Claude Code chokes and produces nothing useful.
Once I realized the problems involved with using an AI assistant, especially how easy it was to let Claude Code do things without fully understand what it was doing, I resolved to make sure I would never become dependent on Claude Code to think for me. So I set a few ground rules for myself to follow to make sure I would always understand how the software was working.
I found that by obeying a few of my own ground rules (discussed later), Claude Code actually helped me learn more about the project I was working on, rather than increasing the project's complexity beyond my understanding. And it helped me learn more about the underlying technologies that make it work — there are things I learned about TypeScript, and even SQL, that I had never known before. And I learned these things at faster pace than I have ever learned anything in my career before. So as long as I am still making a conscious effort to learn and understand, Claude Code has been an extremely helpful tool.
Limitations of coding assistants
Actually, the biggest problem I encountered while using Claude Code was more of a problem with me than it was with the technology. Without even realizing it, I was becoming dependent on the coding assistant to think for me, and this leads to some really big problems. It is really frightening how quickly and and subtly this dependency happened.
I thought that if I were to just read the code that Claude Code wrote, and if that code it seemed to make sense, that would be good enough. I would understand what Claude Code did by reading the code it wrote, and through that higher-level understanding, I would eventually be able to see the "nuts and bolts" of how the system works over time.
But that is not what happened at all. What actually happened was, Claude Code would implement a feature, I would read the code and believe I understood what it had done, then commit the code changes to the Git repository. In fact, I actually had no idea what Claude Code had really done, because it was thinking for me, not just writing code for me. At times I had thought maybe I didn't understand the changes that had been made, whenever that happened I would just ask the coding assistant to explain it better. Then I would read the explanation, I would believe I understood the explanation, I believed the explanation seemed to make sense, so I would move on to the next feature. I did not realize that I had was actually not understanding anything very well at all.
I first became aware of this "thought dependence" when my colleague asked me to explain why I implemented a new feature in a particular way. I was very embarrased to discover that I actually couldn't answer their question because I didn't really implement the feature myself. As I tried to explain I was aware that I sounded like a complete amateur.
The feature in question turns out to be a pretty good case study in why you shouldn't let an AI write code for you without understanding it: there was a feature in Redis that I could have used which I didn't know existed, simply because I hadn't used Redis very often prior to working on this project. Every key-value in the Redis store can be set with a time-to-live (TTL), for cache invalidation. (That makes sense, Redis is a data cache after all.) Claude Code had gone ahead and written the code as if the TTL feature did not exist. Instead Claude Code implemented it's own cache invalidation logic hooked to a Cron Job, all from scratch rather than reusing the already-existing Redis TTL feature. And I didn't know this was a bad design because I had not worked with Redis very often up to that point.
So this is a good example of how even an experienced engineer like myself can end up making these kinds of mistakes when using an AI assistant. If you are unaware of what knowledge you are lacking, you won't be able to prompt the AI assistant in an intelligent way, or to catch the mistakes the AI assistant might make. It doesn't matter how well you know a large and complex system, there are likely always a few details you don't know about and wouldn't understand whether an AI has made a mistake or not. You can mitigate this problem by doing more code reviews, not just with AI, but with real people as well. But that means real people have to read code, you can't hand-off every code review job to an AI.
AI coding assistants only work for a few programming languages
This is because most AI coding assistants are trained on information taken from the Internet, and the though the Internet has discussion forums for every programming language, only the most popular programming languages have the large volumes of discussion material required to actually train a LLM. Less popular programming languages just don't have enough people talking about them to train the LLM to the point where the LLM could ever be useful. You would have to fine-tune a LLM for a given language in order for it to work, and fine-tuning is more expensive.
But AI coding assistants are still useful for most people. Most software development nowadays is in some way related to web app development. So by focusing the training corpus on this domain, the developers of AI coding assistants are sure to produce LLMs that are useful for probably 80% of all software engineers roughly 80% of the time. The last 20% however, who work in more obscure domains, such as for programming languages that are not often used, or systems that require at least some understanding of mathematics (lambda calculus, relational algebra) might not find Claude Code to be quite as useful. This is just the nature of LLMs, it is the same reason why a chess program from the 1970s can still beat an LLM at chess — LLMs are not classical computers, and they can't "understand" complex algorithms or mathematical theories.
Claude Code prefers copy-and-paste over writing reusable functions
When I was in high school, first learning the fundamentals of software engineering, one of the very first things I learned was to avoid copy-and-pasting code. There is a name for it: "DRY," which means "Don't Repeat Yourself," as opposed to "WET," which means "We Enjoy Typing". I was taught very early on that if you find yourself copying and pasting code, what you should do instead is (if possible) write that code into a function. I had teachers in college who would dock points from your homework if they found you had copy-pasted code somewhere.
Well, I tried to explain this to Claude Code by writing it into the CLAUDE.md file, which is a file full of prompting instructions that you want Claude Code to always remember as does its work. But no matter what I do, Claude Code just is not able to avoid copy-pasting. It is simply unable to do it.
And this is a problem you need to solve immediately, as soon as you see it happening. If you see the same code being rewritten in many places. Drop what you are doing and refactor it into a function right away. If you don't do it right away, Claude Code tends to copy-and-paste the code it sees in the file it is working on. So if that file contains poorly written code, that poorly written code will soon be copy-pasted everywhere.
The experience I had was with database queries. I noticed that Claude Code tended to re-use an open database connection by taking the connection object from an undocumented API. This is an ugly hack which can break your program if that API ever changes in a later version of the software packages that your app depends on. What you are supposed to do is use the database client library's public API to reuse a database connection. Amazingly, Claude Code had in fact already written a wrapper function to obtain a reference to an open database connection, and placed it somewhere in the collection of back-end utility functions. But this utility function was used nowhere, and the same exact ugly hack appeared in some 30 different places around the code base. Because every time it needed to create a new query, it would use the querying code from elsewhere in the code base as an example for how to write the new feature, and it would simply copy the ugly hack each time.
It took me a full day of work, but with Claude Code's help I was able to refactor the ugly hack out of the code base entirely. I also put all the database-related functionality into a single file, and made sure that every query returned had a TypeScript type declared for it. Once I had done this, the quality of the code the Claude Code produced also improved. The lesson is: don't ever let bad code accumulate in your code base. (And that is also yet another reason why vibe coding is a bad idea).
Why you should not copy-and-paste code
For anyone who never learned why you should not copy-and-paste code, keep reading. There are many reasons, but the most important are:
When you break your program up into functions, it forces you to label the function, which forces you to name what it does, which helps you to understand what the code is doing. It also helps everyone who reads your code better understand what it does, it makes your code more readable. If code is copy-pasted everywhere, it can obscure the logic of your program, making it look more complicated than it really is because there are more lines of code.
Other people can use your function too, not just you. That can sometimes save other people a lot of time and effort.
If you start to see lots of functions that are sort-of similar in how they behave, you start to see patterns in the problems you solve. This not only makes you faster at recognizing the problem, but also faster at figuring out which function you should use to solve a particular problem. It also helps you to see when you can move similar functions together into a single "library," or reusable unit of code. This also makes programs much more readable and easier to understand.
In general, usually but not always, smaller programs tend to run faster. Larger programs take more memory, and memory access can slow a computer down. So if your program is small, it takes less memory, and the computer has to access memory less often. Even if memory is plentiful and fast, these inefficiencies can accumulate over time and over many uses, especially in the back-end code of a web application which may be serving many thousands if not millions of people.
Ground Rules for using LLM coding assistants
Write detailed prompts, do some of the hard work yourself
The more detail you write into your prompt, the less work the AI coding assistant has to do to solve your problem, the higher your chances are of getting it to succeed at it's task. This can also save you tokens, though this may seem paradoxical since my prompts are probably much longer than what most people would write. It can also save you tokens by keeping the AI assistant on task rather than wasting tokens pursuing impossible or nonsensical solutions.
So if you do some of the hard work of thinking about where the problem is and investigating a bit of how to solve the problem yourself, you can meet the AI coding assistant half way. I find the problem and tell the AI assistant what it is, then let the coding assistant lookup a solution in it's vast "memory" of all the similar solutions that exist on the Internet.
Slow down
You may be under pressure to meet a deadline, you may be tired from working all day. This will tempt you to ask Claude Code to do the work for you. This is how you lose your grip on understanding how things work. Force yourself to work at the pace of your own understanding. Push back on people asking you to work faster. There are stories of people losing their jobs because they were under pressure and failed to check what an LLM was doing for them before submitting their work. Don't let it happen to you.
AI assistants can help you find problems in your code faster, it can answer your technical questions quickly without you having to look-up the information yourself (though try to double-check against real documentation as often as you can), and it is pretty good at refactoring code, or writing some throw-away code to perform a one-time task, which can save you a lot of typing. But an AI assistant will not increase how quickly your brain can learn new things, or how quickly you can reason your way through solving a problem.
So in these ways, yes, AI assistants can help you to work a lot faster — maybe not 2 or 3 times faster, but maybe 50 percent faster. If you do find yourself working 3 times faster, it is likely that you are not actually learning or solving problems on your own, which means you have let the AI assistant do your work for you, which also means you are becoming dependent on the AI to do your work. Don't let that happen, you will probably regret it later if you are asked to explain a decision that you let the AI assistant decide for you.
The author of the "Pi" agentic AI platform once gave a fantastic interview and his experience is almost identical to my own. His advice is also, slow down. Do not let the software grow beyond your ability to comprehend it yourself.
Keep writing code by hand
This may not be good advice for everyone, but it was good advice for me. I realized that the act of actually writing code is what helps me to understand what the code does, and I remember it better as well. Simply reading the code written by Claude Code merely gave me the impression that I understood how it worked when I really didn't.
Actually writing the code myself with my own hands required me to think about what I wanted to do and why. It required me to write the code and learn more about what things I did not yet understand. It forced me to reason my way through solving an engineering problem, and that forced me to understand both the problem and the solution. If you don't know what to write, ask Claude Code for help, but don't let it do your homework, because (like it or not) doing homework is how you learn.
Following this rule of hand-writing my own code also helped Claude Code to avoid the copy-paste problem, because if you don't let it write code for you, it doesn't have a chance to copy-pasting anything.
Ask for more code reviews
If you are following my rule about only writing code by hand, you will make mistakes. Usually it is easy to figure out your own mistakes, but every once in a while you may make a subtle mistake that is really, really hard to solve. Claude Code is pretty good at catching these subtle errors in languages like JavaScript and TypeScript, because it has been trained on tens of thousands of examples of similar problems. Always ask Claude Code to review your code, but don't let it fix the code for you. Read the Claude Code review, and write the changes it recommends with your own hands.
One caveat is that Claude Code is not good at reviewing SQL. It can write SQL well, but in my experience Claude Code is very easily confused by the boundary between SQL and the front-end language. It can copy-paste a variety of different kinds of queries, but understanding relational algebra is just not how LLMs work, so do not expect it to be able to code review your SQL queries beyond catching the most obvious mistakes. It is pretty good at explaining what a query does, however, and that has been helpful.
Also, the suggestions it makes for structuring your data are usually not very good. You need to have a good understanding of your problem domain, and you need to model the data relations yourself, or you will end up wasting a lot of tokens.
It is also helpful to /clear the context (make it forget everything it was working on) and ask it to review all of your code for problems. It tends to overlook issues that are unrelated to the recent conversation you have had with it, so /clear-ing it's context helps it write better reviews. I recommend you do this at least once, maybe even twice before merging a new feature into the main branch.
Ask Claude Code to write tests that fail
A principle of logic is that to avoid affirming the consequent, test that a claim is true and also that the converse claim is not true. The software debugging equivalent of this principle is to first write a test that should pass but does not, proving that the bug exists, and then fix the bug and show that the test passes. This is something you should do even if you are not using an AI coding assistant. But when using an AI coding assistant to find bugs, make it prove that the bug exists with a test case. Then when you ask the assistant to solve the bug, you can prove the bug is fixed by running the program and seeing if the test case passes.
Though I would not recommend using an AI coding assistant to write all tests for you, it is certainly good at writing tests to find bugs, once you have some evidence that a bug exists. It also helps to ask the AI coding assistant to write sanity checks for your code, and requiring the coding assistant to ensure all tests pass when making changes to the code. This keeps the coding assistant from writing too much slop.
Ask Claude Code if a solution already exists.
As I said earlier, one limitation of AI coding assistants is that they tend to prefer copying and pasting known answers to your problem, asking Claude Code to find an existing solution will help prevent the size of your code base from growing unreasonably large.
Claude Code tends not to try to find functions in your program that may have already solved a problem, it prefers to write code from scratch. This has to do with saving the amount of tokens it needs to ingest. Searching through a file for existing solutions requires a lot of reading of input, and that costs money. It can take shortcuts with tools like grep or using similarity search (Retrieval-Augmented Generation). But if you don't explicitly instruct it to do so, it will just write code from scratch, as that is generally an easier thing for it to do. And this will make the size of your code base explode in size, and that will in turn make it more difficult for the coding assistant to work on your software.
But this is also sound advice for any engineer: don't re-invent the wheel. If you have an engineering problem to solve, do not ask Claude Code to solve the problem for you, or else you may not be making enough effort to understand the solution. Instead, ask Claude Code to investigate the problem, and hypothesize solutions, see if the output to these prompts indicates that there is enough context for the solution to be inferred, and ask it to write code in small chunks that you can review at each step.
Furthermore, you should not ask the AI assistant to solve the problem because it gives it an opportunity to copy-and-paste a solution from it's training data rather than reuse code that already exists. Instead, ask it what are some common solutions to the problem you are trying to solve. Once Claude Code explains to you the potential solutions, pick one and work it into your code yourself, then ask Claude Code to review your code. If you instead let Claude Code solve the problem for you, you will likely not really understand the solution, you will only think you understand.
Appendix: using Claude Code in Emacs
I didn't take me very long to figure out how to get Claude Code to work in Emacs. I was using the Claude Code IDE for Emacs application, it is a wrapper around the claude-code command line tool, and it also listens in on some other port to receive editor instructions (I believe the claude-code command line tool runs MCP over a localhost TCP port, I am not certain about this though), so that Claude Code can issue commands to Emacs to create and edit files, and run shell commands like find, grep, sed, patch, git, ls, ps, and and so on. By issuing these commands, it can gather information about your source code into it's "context" and improve it's accuracy. By default, it will always ask your permission before running any shell commands. A lot of people turn off the "ask permission" feature, but I could never do that myself, I still don't trust it that much. I always inspect every single shell command it runs.
(use-package vterm
:vc (:url "https://github.com/akermu/emacs-libvterm" :rev :newest)
:ensure t
)
(use-package claude-code-ide
:vc (:url "https://github.com/manzaltu/claude-code-ide.el" :rev :newest)
:bind ("C-c a i" . claude-code-ide-menu) ; Set your favorite keybinding
:config
(claude-code-ide-emacs-tools-setup)
)
The Emacs Claude IDE requires you install VTerm because VTerm is the only Emacs terminal emulator fast enough to render the constant redraws that the claude-code CLI application does. The Claude Code IDE is a Node.js application that uses using Ink to output to the CLI console. Ink renders DOM elements using React, and draws these DOM elements as ANSI terminal text (ASCII art with color), and can also wait for user input and do tab completion.
Unfortunately, the claude-code CLI application has it's own unique set of key bindings, and though these key bindings are mostly similar to many other popular software applications (including Emacs), it is still running in VTerm, so you do not have the full set of Emacs editing tools at your disposal.
To mitigate this problem, I wrote my own Emacs interface to the Emacs Claude Code IDE — nothing complicated, just a way to pop-up a blank editor window, write a prompt, and send the prompt to the Claude Code IDE window.
(defvar ramin/claude-prompt-close-buffer-on-send nil)
(defun ramin/claude-prompt-buffer-send ()
"Send the contents of the \"*claude-prompt[
The C-c C-c key binding is also used
by wdired-mode and occur-edit-mode when
committing changes made in the editor buffer, so it is natural to use
the same key binding for my own Claude Code prompt window.