Reflections on building Captain, a blog engine, with AI 🚀
Lately, you might have seen countless videos claiming, “How I built this e-commerce website in 30 minutes with AI” or “Build a website from scratch without any coding knowledge.” But the reality? It’s often far from these grandiose promises.
In this post, I’ll share the challenges and wins I encountered while developing Captain, the blog engine you are reading this article on, peeling back the curtain on what it’s really like to build something meaningful with AI.
The Genesis 🌱
When I started, my use of AI was primarily limited to code completion—relying on dynamic suggestions from VSCode’s Copilot rather than elaborate prompts. What stood out to me was AI’s ability to handle documentation and test generation. These features allowed me to focus more on coding while having peace of mind that the documentation would remain consistent and up-to-date.
Captain began as a private project using VSCode and Claude 3.5 Sonnet, shortly after its public rollout. Eventually, I decided to open-source it, curious to see if AI could help me reach the point of a usable, maintainable product.
There’s an ongoing race among tools like Bolt.new, Cursor, Windsurf, Visual Studio Code, and Loveable. They all claim to revolutionize how we write code, often spotlighting the ease of building an app in mere minutes. But what happens when you go beyond quick demos?
- How do these tools handle refactoring?
- Can they adapt when the project takes a new direction?
- Do they truly understand how a small change can ripple through an entire codebase?
In this article I will talk about the pros and cons of building a Golang, no-installation, Gin powered blog engine.
The Pros âś…
1. Enhanced Productivity
When used correctly, AI significantly boosts productivity. Tasks like generating boilerplate code, analyzing test failures, and writing documentation are faster and smoother.
But not all editors are created equal. For instance, switching from VSCode to Windsurf made a noticeable difference:
- VSCode: Slower generation, frequent failures, and less integration.
- Windsurf: Seamless workflows like analyzing failed tests by running
make test
and providing actionable solutions.
2. The Art of Precise Prompting
When working with AI tools, the quality of your outputs often depends on how you frame your prompts. Small differences in wording can result in drastically different outcomes, both in quality and alignment with your goals.
After implementing local disk uploads in Captain, I decided to add S3 support. Starting with a new git branch, I tested these exact two prompts as a starting point:
Prompt A: “Add media upload for S3-compatible services.”
This seemingly straightforward request led to AI attempting to shoehorn S3 upload functionality into the existing code. The result was convoluted and unworkable—clashing with the existing architecture and introducing a tangled web of bugs and regressions.Prompt B: “Abstract storage functionality and add support for S3-compatible services.”
With this version, AI generated a clean abstraction layer, restructured existing storage code, and implemented a dedicated S3-compatible module. The result was not only functional but also extensible, adhering to best practices in software design.
The difference? Prompt B provided context and emphasized abstraction over mere addition. By framing the task in terms of desired outcomes rather than specific changes, the assistant was able to generate code that aligned better with the project’s architecture.
Precision Beyond Features
It’s not just about features; precision matters in workflows involving documentation, tests, or configurations. For example:
- A vague prompt like “Add this feature” might generate only the core functionality, overlooking ancillary elements like documentation or configuration updates.
- A precise prompt like “Add this feature, update the relevant documentation, and modify configuration files to support it” ensures all aspects of the implementation are addressed.
This level of precision transforms the AI from a “guess-and-check” assistant into a more reliable collaborator. However, achieving this requires clear communication and a deep understanding of the task at hand.
The Lesson
Prompting is not a one-size-fits-all skill—it’s an iterative process. The more context and clarity you provide, the more likely AI is to produce meaningful, high-quality results. While AI is a powerful tool, its effectiveness is limited by how well you guide it. In other words, garbage in, garbage out.
3. A Catalyst for New Projects
Would I have built Captain from scratch without AI? Probably not, I would have likely stuck with traditional blog engines like Hugo or Tunalog (which I was using before). AI made the process faster and more approachable, even if it wasn’t entirely effortless.
The Cons ❌
1. Context Loss
After creating several admin template pages, you’d expect the assistant to grasp the project’s structure. Yet, new templates were often generated with Tailwind classes—despite Tailwind never being part of the project.
Occasionally, AI-generated code would inexplicably remove working sections, introducing regressions that had to be “manually” fixed.
2. Context Misses
When asking for an admin page handler with a form, AI repeatedly made mistakes:
- It would generate HTML (often involving Tailwind, for no reason) with an HTML form in it
- The form action would point to the new handler, but the handler expected JSON (
application/json
) instead of form data (application/x-www-form-urlencoded
) and thus incorrect API calls likeContext.BindJSON
instead ofContext.PostForm
were used.
These mismatches highlighted AI’s limited understanding of context across related components. Even after a good amount of the codebase was already highlighting what I wanted.
3. Incomplete or Erroneous Code
Sometimes, generated code felt half-baked. For example, adding a configuration panel required listing Timezones and Chroma styles to present them in a <select>
. This is what was generated:
// GetTimezones returns the list of available timezones
func GetTimezones() []string {
return timezones
}
// GetChromaStyles returns the list of available syntax highlighting themes
func GetChromaStyles() []string {
return chromaStyles
}
When the correct implementation was:
// GetTimezones returns the list of available timezones
func (c *Config) GetTimezones() []string {
return timezones
}
// GetChromaStyles returns the list of available syntax highlighting themes
func (c *Config) GetChromaStyles() []string {
return chromaStyles
}
If you don’t know Go, the first code extract defines two functions, the second one defines struct methods.
4. Code Shadowing
The models often struggle to detect when one symbol might shadow another. This occurred multiple times, but here’s one example: While implementing Markdown support, AI generated imports that ended up shadowing each other:
import(
...
"github.com/alecthomas/chroma/formatters/html"
"github.com/gomarkdown/markdown/html"
)
5. Security Risks
One AI-suggested implementation for session cookies was alarming:
c.SetCookie("session", email, 3600*24, "/", "", false, true)
This meant anyone could easily craft a cookie with the name session
, guess a valid email and gain authenticated access—clearly unacceptable. Also, the third and second to last parameters of SetCookie, are the domain
and secure
parameters, those should be configurable for production use:
A cookie with the Secure attribute is only sent to the server with an encrypted request over the HTTPS protocol. It’s never sent with unsecured HTTP (except on localhost), which means man-in-the-middle attackers can’t access it easily.
But despite having an external configuration already in place, the agent never suggested to implement these parameters as configuration options.
Conclusion đź’
AI is a powerful tool, but it’s far from infallible. Creating a real, marketable project from scratch with just prompts remains an open question — one I’m excited to explore further. You have to be careful about the direction the code is taking and be very attentive to the output: AI is a very talented programmer with a limited attention span, who’s going to do exactly what you ask and nothing more.
- Is AI a game-changer? Absolutely.
- Will developers be replaced soon? Probably not.
- Does it require careful crafted prompts to be effective? Yep.
- Would I trust AI-generated code without thorough reviews? Not a chance.
- Can someone without coding knowledge create a marketable product with AI? Unlikely.
I’m convinced that having a solid understanding of the language, domain, or environment you’re coding for is essential if you want a codebase that’s not only maintainable but also readable. Ultimately, you may not write much code, but you’ll end up reading a lot of it.