In this post, I cover the five core principles for effective AI agentic engineering. The golden rule? Always "be the boss." From writing a concise AGENTS.md/PLAN.md/prompt to staying skeptical and managing expectations.
Also keep in mind it does NOT matter if you are senior/junior developer.
If you’ve been experimenting with AI coding assistants (Claude, Cursor, etc.), you've likely experienced both wonder and frustration 😉. For me there were times when the assistant one shot a feature in minutes, and other times when it got stuck in a loop of repeated mistakes.
Spend Time on Your AGENTS.md
This is your project's command center. Keep it concise and structured around:
- The style of your project: define the coding style, architecture, and approach.
- A short intro into the project/repo.
- Project structure.
- Testing strategies.
- Limitations and constraints you want LLM to always keep in mind.
I have seen this example on GitHub which can give you a good sense of what you might wanna have. But keep in mind it is not Bible.
Spend Time on Your Prompt
| Section | Purpose |
|---|---|
| Spec | Clearly define what needs to be built. |
| Success Criteria | State exactly how success will be measured. |
Work Incrementally
Stay disciplined:
- Start simple.
- Work step-by-step.
- Validate success criteria after every major change.
- Create a feedback loop: Test → Fix → Iterate.
- Iterations can be adding more features, cleaning up, addressing known bugs or todos.
This way we ensure that we never progress more than a few steps beyond a known-good state.
But before going into more details I would like to first have a definition for:
- Complicated/deterministic: many parts, but the relationships between them are sufficiently predictable that expertise can determine how to solve the problem.
- Complex/adaptive: many interacting parts whose behavior changes in response to one another, so you cannot reliably determine the solution in advance.
You might have heard of "don't boil the ocean" idiom. Based on what I have read this is a good advice for deterministic problems but NOT for adaptive problems. And honestly I believe it is spot on with a small deviation, we will talk about this shortly.
I just like to start with a some examples of what I consider complex and what is complicated in this context:
- Complex/adaptive problem to solve:
- How does this entire system behave under changing traffic, failures, retries, recovery, scaling, network availability, and the behavior of hundreds of independent robots?.
- Complicated/deterministic problem to solve:
- We need idempotency because RabbitMQ may deliver an event twice.
- How do we guarantee a data isn't lost if the robot loses power during upload?
- How do we ensure event ordering in an event-driven architecture?
- How do we implement exactly-once event processing in an event-driven architecture?
So, for adaptive problems, you need to discern the overall architecture and consider what might go wrong - obviously, we can't cover or anticipate every single scenario - but you should still try to form a coherent plan that addresses the most likely issues.
Once you have that plan, you should be able to break it down into smaller, implementable steps. This is what I meant by "a small deviation". However, now that I think about it, I suppose you might be applying the same approach even to complicated/deterministic issues, since this way of thinking helps you maintain a broader perspective.
Deterministic Problems
So if I had to give you a concrete example I would say "migrating a 10 TB PostgreSQL database to a new cluster with zero data loss and less than 30 seconds of downtime." is a deterministic/complicated problem since there may be:
- Thousands of tables.
- Replication.
- Custom extensions.
- Enormous datasets.
- Legacy applications.
- Tricky dependencies.
- Performance constraints.
- Rollback requirements.
The important thing is that the problem doesn't fundamentally change its nature because I am solving it.
Here we can analyze the system, understand the dependencies, develop a procedure, test it, and execute it. There is a discoverable technical solution and for sure we can break it apart and solve it incrementally (remember, divide and conquer).
Adaptive Problems
Imagine you are developing a solution for robots to inspect industrial equipments. The pipeline would be:
Robot
↓
Upload inspection
↓
Backend
↓
"InspectionUploaded" event
↓
Message broker
↓
Processing service
And suppose you have 500 robots. Normally:
100 inspections/minute
↓
100 events/minute
↓
Downstream processing service keeps up
Everything is fine. Then your downstream processing service becomes temporarily slow. Perhaps its database is overloaded. So processing goes from:
100 inspections/min → 60/min
The queue starts growing. So you have:
Incoming: 100/min
Processing: 60/min
Queue growth: +40/min
It is still fine and manageable. But now something interesting happens. The downstream processing service has a timeout. When processing takes too long, it assumes failure.
So it retries. Now you aren't actually receiving 100 pieces of work per minute anymore. You're receiving:
100 new inspections
+
40 retries
+
some additional retries from previous failures
Now the service is trying to process 150-200 operations/minute. That makes the database even slower. Which causes more timeouts. Which causes more retries. Which creates even more load.
Now add the robots. This gets even more interesting. Suppose robots normally upload immediately. But a factory has terrible Wi-Fi. So robots store inspections locally and upload them when connectivity returns. At 10:00 AM, connectivity comes back.
Suddenly:
Now you might say "Every component has sensible behavior. Why is the overall system behaving terribly?".
Because the interaction between individually reasonable mechanisms can produce behavior that nobody intended.
And this will be reported as a bug ticket with a description like this:
At 10:37 AM, the system entered a state where throughput collapsed, retries exploded, queue depth increased exponentially, autoscaling oscillated between 20 and 200 instances, database resource usage spiked, and the system recovered 45 minutes later after traffic naturally decreased.
That's a complex behavior, there may not be one broken component. Instead: A → influences B → influences C → changes A.
And the overall behavior emerges from those interactions.
Don't Get Lazy -- YOLO Trap
LLM gave you a few wins and then you're overconfident in its ability. Suddenly, you ask it to YOLO a big task and come back later, hoping everything worked.
Spoiler alert, it probably won't.
- Challenge your assistant, even when it sounds confident.
- If the AI says it fixed a bug, ask for evidence, root cause, and test results.
- Don't let your guard down, unless you want your project to derail 😉.
Manage the Frustration
There will be times when the model makes the same mistake repeatedly, despite explicit instructions. You'll want to pull your hair out.
That's normal 🥲🥹.
These moments are not signs of failure, they're signs of limitations in the model or hitting context window limitation (imagining the model is NOT compacting it for you automatically). Your role is to detect, manage, and work around them.
- Anticipate chaos.
- Treat frustration as a signal to reset or reframe the problem.
- Keep going, this is where real growth happens.
Advise for
- Junior engineers:
- This is a learning tool - not a replacement for understanding.
- Challenge the AI with questions so you can follow what's happening.
- Stay skeptical. The AI can be confidently wrong 😂.
- Senior engineers:
- You can already smell BS when the AI makes it up (I imagine you are looking at what it is writing).
- Use the tool to do more, not to replace your joy of building.


Top comments (0)