I have developed a workflow that I call:
A semi-automated, AI-native development flow with a human in the loop.
It may sound like a collection of AI buzzwords, but each part represents a very practical principle.
1. Semi-automated
If a task can be performed reliably by a simple script, it should not be delegated to AI.
- Want consistently formatted code? Configure Prettier.
- Want custom naming conventions, import restrictions, or architectural constraints? Configure ESLint or write a custom rule.
- Want consistent formatting across different editors? Add an EditorConfig file.
Do not make AI spend context and reasoning capacity checking indentation, semicolons, or naming conventions. Deterministic problems should have deterministic solutions.
Here is a less obvious example.
I have written several thousand unit tests for my current project. As a perfectionist and former QA engineer, I care about how elements are selected in tests.
Whenever possible, tests should use accessible selectors first, such as roles and accessible names. A test ID should only be used as a fallback.
Initially, I treated this as something AI should remember, while I would catch mistakes during code review.
But I noticed that I was correcting the same issue far too often.
So I asked myself:
Why am I repeatedly reviewing something that can be checked automatically?
I wrote a custom lint rule.
A few lines of code solved the problem permanently.
Now the linter knows the required selector priority and reports violations automatically.
The best part is that AI fixes these issues in virtually every case.
Why?
Because running the project’s validation procedures is mandatory in my workflow. Even when AI makes a mistake, it runs the linter, receives a precise error message, and corrects the code.
That is what “semi-automated” means to me:
AI handles tasks that require reasoning.
Scripts handle tasks that require consistency.
2. AI-native
A repository can be structured primarily for human convenience, or it can be structured so that both humans and AI can navigate it efficiently.
An AI-native repository treats context as a limited engineering resource.
Some developers are familiar with code golf: solving a problem using the fewest possible characters.
AI-native development is not exactly code golf, but the underlying constraint is similar:
Every token has a cost.
This does not mean compressing the code until it becomes unreadable. It means finding a balance between readability, consistency, and context efficiency.
Project standardization plays a major role here.
One component should not use a completely different structure, style, or paradigm from another component without a good reason.
Reusable abstractions should be clearly defined and used consistently.
This allows AI to find an existing implementation, understand it as the project standard, and use it as a reliable reference.
Another important principle is what I call context segregation.
A feature should be divided into meaningful files so that AI can load only the context required for the current task.
For example, a test implementation may have:
- the test file itself
- fixtures
- mocks
- setup utilities
Sometimes the setup is larger than the actual test suite.
When I only need AI to inspect test names or understand what behavior is covered, there is no reason to feed the entire setup into its context.
I use colocated suffix files for this purpose. A larger feature can have several supporting files placed next to it, with clear suffixes describing their roles.
Depending on the task, I can provide AI with exactly the files it needs—and nothing else.
And please, use barrel exports where they make architectural sense.
There is no reason to repeatedly feed AI long absolute import paths when a stable public module API can expose the same components with fewer tokens and less coupling.
3. Human in the loop
The internet currently seems obsessed with fully autonomous pipelines where an agent writes code around the clock without human participation.
Maybe I am old-fashioned, but I am highly skeptical of that approach.
I could list many examples of what AI misunderstands, what must be reviewed, and where autonomous implementations fail.
But there is one issue that matters more than all the others:
If you remove yourself from the process, you stop LEARNING.
- You are no longer collecting feedback.
- You are no longer examining what AI understands and what it misunderstands.
- You do not see which instructions work, which create confusion, where AI is fast, where it struggles, or where an additional tool could eliminate an entire category of mistakes.
Most importantly, you lose the data required to improve your own development framework.
Here is my favorite example.
At one point, I created what I believed was the perfect AI development process.
I reviewed every instruction repeatedly. I refined every detail. I was convinced that the framework was exceptionally well designed.
Then I used it in production.
Within two months, the process went through five generations of fundamental changes—not counting dozens of smaller improvements.
Why?
Because real-world usage generated feedback.
I observed what failed, what created unnecessary work, what AI interpreted incorrectly, and what could be automated more effectively.
No matter how good your prompt, instruction set, agent configuration, or MCP setup appears, you do not have objective evidence that it works until you observe it under real production conditions.
That is why my workflow keeps a human in the loop.
Not merely to approve AI-generated code.
But to learn from every iteration and continuously improve the system producing that code.
Top comments (0)