Sunday, August 3, 2025

 

AI Code Assistant Tooling: Initial Perspectives from a Developer

I’m writing this article to share my first impressions of using AI code assistant tools during software development.

First, I should mention that I’ve been involved in software development for over 30 years. I’ve worked on mainframes using COBOL/JCL and IMS databases, all the way to the latest Spring Boot frameworks in Java and Kotlin. I’ve programmed in Python, PHP, Perl, C, C++, and Bash. I’ve practiced waterfall, agile, TDD, and BDD. Over the years, I’ve used various databases, IDEs, and text editors like vi and emacs.

Based on this experience, I feel I can speak credibly about what software development entails—and how AI tooling is beginning to shape it.


1. AI Doesn’t Replace Developers

First and foremost, I don’t see AI tools replacing developers. Quite the opposite—AI needs to be driven by a skilled developer to produce the right results. In my experimentation with Claude Code, I encountered many scenarios where domain expertise was critical. Without my background in various frameworks, databases, and design patterns, the responses would have been inadequate—or even counterproductive.


2. You Need the Skills to Drive AI Tools

AI tools don’t inherently know what tech stack you’re using. That’s something the developer must specify in the prompts. This already requires experience and judgment.

Software that goes to production must meet specific standards for maintainability. Therefore, the developer—not the LLM—should dictate the programming language, frameworks, databases, design patterns, and build tools. Teams must define a focused set of technologies, and then ensure the AI generates code accordingly. Again, this reflects the essential role of the developer.


3. Functional Requirements Must Be Documented

LLMs are trained primarily on public data, like GitHub repositories. But most company-specific business logic is not public. AI cannot infer these internal processes unless they are documented and provided as part of the prompt.

Ironically, this brings us back to something similar to the waterfall model, where upfront documentation was emphasized. In contrast to the agile mantra "the documentation is in the code," we now need clear, detailed specifications—for the AI to generate meaningful code.


4. Be Explicit and Precise in Prompts

Precision matters. In one case, I forgot to specify that the project should use an H2 database for development. As a result, the generated application failed at runtime when trying to connect to a non-existent database server.

This is where developer troubleshooting skills come in. I had to recognize the issue and prompt Claude to include H2 as a development database.


5. Unexpected Compilation Errors

In another case, Claude told me a task was complete and successful—but one of the classes had a compilation error due to a missing type or unresolved symbol.

Once again, it took a developer’s eye to identify and fix the issue. I had to provide Claude with a prompt specifying the file and the line number needing correction.


6. Unexpected Runtime Errors

In one instance, Claude said the application was running and the Swagger UI was accessible. It wasn’t. I received a “site can’t be reached” error.

In another case, Claude indicated a public endpoint was available, but I was getting HTTP 403 (forbidden). I had to guide Claude to modify the endpoint’s access permissions.

These are real-world examples of how developer involvement is still essential.


7. Some Tasks Are Better Done Manually

I’ve also learned that it’s sometimes faster to just do certain tasks manually than to prompt the AI. For example, running a Gradle command or editing a README.md file can often be done in seconds—while Claude might take minutes or generate incomplete results.

Knowing when to intervene manually is a skill that comes from experience with build tools, running local databases, and reviewing running processes.


8. The Good: Well-Structured Code

On the positive side, I was impressed with the code generated for a user microservice. Claude applied good software design principles: separating controllers, services, DTOs, repositories, and security layers.

Having designed and built many microservices myself, I saw a strong resemblance between the structure I would have created and what Claude generated.


9. The Tests Were Lacking

That said, the tests generated were always passing—which is actually a red flag. Upon review, I saw they didn’t cover edge cases. This suggests that while the test scaffolding may be helpful, it can’t be relied upon for thorough coverage.

More work is needed in this area, and developers must continue to play a key role in writing meaningful test cases.


10. LLMs Struggle with Legacy Systems

Having worked in mainframe environments with COBOL, JCL, Mark-IV, and IMS, I seriously doubt AI tools will be of much help there. Much of that codebase isn’t publicly available online, and LLMs are not trained on it. So, for now, don’t expect AI to assist much with legacy systems like those.


Conclusion

LLMs can accelerate code generation, but skilled developers are still essential. Crafting good prompts is crucial to getting useful results.

At the end of the day, computers are simple machines, responding to 1s and 0s through layers of logic gates. Everything above that—frameworks, languages, interfaces—is built by humans to abstract complexity.

LLMs are just another abstraction layer. They won’t replace the need for deep knowledge, critical thinking, and engineering judgment. And there's still a great deal of institutional and domain-specific knowledge that LLMs will never have access to.

In short, AI tools are helpful copilots, but we’re still in the driver’s seat.