AI writes demos quickly, and revisions are also really fast.

Recently, I started a small C++ project using AI. The most frustrating moment isn’t when it can’t write the code, but when it spits out a seemingly decent directory structure in three minutes and casually throws in a few third-party libraries, making the demo actually runnable. That’s the problem. You haven’t even figured out what the newly introduced library supports, how the compilation link works, or where its boundaries are, so rework is basically inevitable later on.

I’m increasingly feeling that what AI programming fears most is not the model being dumb, but starting too greedily. Especially with languages like C++, which don’t have much scaffolding to fall back on; if you miss one step upfront, later you have to account for several steps regarding compilation, linking, library versions, and directory structure.

My own conclusion on this is quite direct: AI is better suited as a pair programmer that can accelerate progress, rather than one that takes over all the initial design and dependency decisions for you. GitHub’s documentation suggests starting with simpler tasks, letting the agent master areas with clearer boundaries like bugs, documentation, tests, or technical debt; Anthropic’s Plan Mode documentation feels more like insurance for complex changes—first read the code, then create a plan, and only then decide whether to implement the change. The two approaches aren’t exactly the same, but they point in the same direction: don’t throw the hardest, messiest, most dependency-laden chunk at the AI right away.

So, this article won’t talk about a bunch of specifications; instead, it will provide a small case study that you can practice with immediately. You can follow along using any empty C++ repository.

It’s not that AI is impossible, it’s that the starting point was too greedy

Writing AI projects, especially in C++, is often a very rudimentary approach.

  • Start with a minimal working version from main.cpp
  • First, get the compilation pipeline running
  • Then decide which libraries to include
  • Ensure it still compiles at every step
  • Wait until the business outline is clear before starting refactoring

This method looks slow, but it’s actually very stable. Because at every step you know exactly what you gained, and you also know which step the problem originated from.

AI is prone to disrupting this rhythm. It really likes to “complete it for you” in one go:

  • I’ve also set up the configuration files for you.
  • I’ve also extracted the logging module for you.
  • I’ve also organized the error codes, utility classes, and directory structure together.
  • It even selected the integration method for third-party libraries for you.

The result is that the demo looks complete, but your mental model is empty. If there’s any discrepancy between a library’s capability boundary and what you expect later on, rework won’t be in one spot, but a series of them.

Start with a small project to get some practice

I think it would be good for practice to build a very small log scanning tool. You can name it anything, like logscan.

It does one thing: it scans log files in a directory, counts the number of error and warn, and then prints the result. This problem is not big, but it’s just enough for you to go through the most common parts of a small C++ project:

  • main and parameter entry point
  • Output formatting
  • Logging
  • Configuration files
  • Business module decomposition

The key is not how advanced the questions are, but how you break them down.

I will break it down into four steps, and for each step, the AI is only allowed to do the current small part.

Step One, Prepare the Output First

First, don’t touch the logs, don’t touch the configuration, and don’t think about abstraction.

Do two things:

  • Create a minimal main.cpp
  • Include fmt, and print the scanned directory and statistics results

The official documentation for fmt provides very clear CMake usage. It has two targets: fmt::fmt and fmt::fmt-header-only. The documentation also explicitly recommends the compiled version, and the reason is quite simple: it’s more friendly to build times. CMake’s FetchContent documentation is also very suitable for starting new projects because it fetches dependencies during the configure stage, rather than downloading them temporarily during the build stage.

In this step, I will directly fix the dependency strategy and not let the AI play freely.

  • Either unify FetchContent
  • Or unify find_package
  • Don’t use vcpkg one moment, add_subdirectory the next, and manually copy headers after that

If it’s a practice project, I usually constrain the AI like this:

Do not optimize the architecture all at once.
The current goal is only this step, and the project must always be compilable.

This step involves only 3 things:
1. Include fmt in the root CMakeLists.txt.
2. Use fmt in main.cpp to print the passed directory and scanning results.
3. Keep the existing target names and directory structure; do not add new logging, configuration, or testing frameworks.

First, give me a modification checklist, then provide the code.
After completion, provide the compilation command and expected output.

There’s a detail here that is quite important. If AI is not constrained, it can easily treat “doing something extra in the back” as a bonus point. You must explicitly tell it that it cannot go beyond the scope this round.

Step Two, Upload Logs

Once the first step is done, it can be compiled, run, and the output looks clean, then add spdlog.

The official spdlog README is actually very straightforward about what it can do. It can handle console logging, as well as regular files, rolling files, daily rotation, and even a backtrace ring buffer. The problem is this: with so many capabilities, AI gets easily excited and wants to cram everything into the first version—colored console output, rolling logs, daily archiving, global logger factory, all at once.

No need.

For this step, I will only have it do console logging, or at most add the simplest file logging. Because what you really need to learn at this point is not “how to design a complete logging system,” but three things:

  • Where to initialize the logger
  • How to get the logger in business logic
  • How to separate logging and normal output when an error occurs

If you immediately implement rotating_file_sink or daily_file_sink, while you quickly gain a “feature-rich” logging module, you might not actually know if you need rotation or daily splitting, or if printing to the terminal during debugging is sufficient.

To put it plainly, understanding how to use spdlog::info() is more important than making the logging factory look pretty.

Step Three: Touching the Configuration File

The configuration file section is easier for AI to turn into a large project.

I prefer to practice with a simpler library like toml++, not because it’s the “most powerful,” but because it’s very suitable for the initial stages of a new project. It is itself a C++17 header-only TOML parser, and the examples in the README are also quite short; you can directly read with toml::parse_file("configuration.toml"). More importantly, its single-header mode is incredibly convenient. The official description is quite interesting: the single-header approach is just “throwing toml.hpp into your source tree,” and then there’s “no second step.”

For this step, do not use a “Configuration Center,” but read three fields:

  • Scan Directory
  • Keyword List
  • Write Output to File

Then put them into a very thin AppConfig struct.

That’s enough.

A lot of rework, it actually comes down to this. You haven’t even figured out if the configuration should be read once at startup or hot-updated at runtime; whether it’s only for the local CLI or if you plan to reuse it for service processes later. AI has already taken five or six layers ahead of you. If you change direction later, all that “complete design” from before will just become a burden.

Step Four: Decomposing Business Modules

For libraries like fmt, spdlog, and toml++, please read through the official documentation yourself, run them successfully in practice, and then ask AI to help you break down the modules.

When you break it down like this, then you’ll have a clear understanding.

I usually receive the following types of files:

  • main.cpp is only responsible for assembly
  • app_config.{h,cpp} is only responsible for configuration reading
  • logger.{h,cpp} is only responsible for log initialization
  • log_scanner.{h,cpp} handles business logic

Note that the splitting happens at this step, not from the first step. In the first step, just lining up the table of contents neatly is often only visually pleasing and doesn’t necessarily mean it’s cognitively clearer.

What’s truly useful isn’t some complex specification

Many online AI coding guidelines are very complicated, with dozens of rules. It’s tiring just looking at them. The ones I kept for myself are actually only three, and they are enough.

First, create a “resource card.”

Every time I prepare to reference a third-party library, I first ask the AI to answer these four questions:

  • What problem does this library solve?
  • How far has the official support reached?
  • Which 1 or 2 capabilities am I planning to use this time?
  • What new constraints will it bring to building, deploying, and running?

If you can’t explain this card, don’t play it yet.

Second, only let the AI pass one checkpoint at a time.

For example, in this round, it is only allowed to complete:

  • Can code
  • Can run
  • Can see expected output

Don’t add things like “while I’m at it, let’s refactor the directory,” “I’ll quickly add a configuration class,” or “let’s unify the exception handling system” in the same round. In C++ projects, the more “conveniently” you do something, the easier it is to muddy the source of the problem.

Article Three, a rollback point must be left after every round.

Even if you don’t commit, you should be able to answer:

  • What exactly changed in this step?
  • If it’s wrong, which parts need to be deleted?
  • After reverting to the previous step, can the project still be compiled?

You will find that this line of thinking is actually very similar to manual development. The difference is that before you wrote the code yourself, now you are supervising the AI so it doesn’t run wild.

A More Practical Use Case for AI

If you really want to master this set of things, I suggest doing three consecutive rounds.

In the first round, only let the AI help you with fmt, and read through the CMake configuration and output code yourself.

Round two, only let the AI handle spdlog. You decide whether it should be console logging or file logging; you cannot use both together.

Round three, only let the AI handle toml++, and you decide the configuration items yourself; do not allow the AI to guess future requirements for you.

After going through these three rounds, your feeling about AI programming will change a lot. Before, it was “how did it generate so much for me all at once,” but later it will slowly become, “how small should I prompt the next step to be most worthwhile.”

I think this is the most valuable skill in AI programming right now.

It’s not about creating a demo.

but rather to steadily move a project forward.

To be honest, this set of methods isn’t very novel; it’s even a bit old-fashioned. But old methods are often resistant to rework. Especially with a language like C++, if you let the AI do less and confirm more yourself at the beginning, things will genuinely become much easier later on. Otherwise, you might fly up in three minutes only to have to go back to the drawing board for three hours—and the one who looks foolish is still yourself.

References

Writing Notes

Original Prompt

Prompt: Writing a project manually, especially for languages like C++ that don’t have scaffolds. First, create a basic framework starting from main and gradually expand the logic by adding modules, such as configuration files, logging modules, and iterative business modules. My habit is to ensure it compiles at every step and refactor incrementally; what I write initially might not be the optimal solution. Regarding AI programming, often nowadays, steps are skipped, and too little thought is put into the early stages, especially when introducing new third-party libraries. While AI can quickly generate a demo, because I don’t fully understand the details of newly introduced third-party libraries or what functions they support, the rework rate for related logic is significantly higher than manual development. Do you have any good practical examples that I can learn from? I know there are many complex AI programming guidelines online, but I want something simpler and quickly implementable.

Writing Idea Summary

  • Narrow the topic down to the tooling lane, focusing on where things become easier or conversely more awkward after AI changes workflows.
  • Don’t write a general checklist of best practices; instead, turn it into an incremental hands-on case study using a small C++ project.
  • Keep the core judgment that the user “starts from main, can compile at every step, and refactors gradually,” and make this the main narrative thread throughout the article.
  • Supplemented with primary sources for Anthropic, GitHub, CMake, fmt, spdlog, and toml++ to avoid guessing about tool capabilities and dependency methods.
  • The article structure progresses according to fmt -> spdlog -> toml++ -> module decomposition, intentionally preventing AI from achieving everything in one go.
A financial IT programmer's tinkering and daily life musings
Built with Hugo
Theme Stack designed by Jimmy