I hope everyone is having a productive year, I’m trying to keep my productivity charts trending up right behind yours. And I wondered how this productivity looks like for non-tech folks who never saw git (and prolly will never see it, just a github page maybe).
How well can the folk fare without engineers? Engineers are now replaced by LLMs, aren’t they?
Plenty of scope has been charted in these territories recently, on the side of totally new projects. I had a ~200k line PHP5 Zend Framework© enabled codebase in middle drawer. So it was decided to feed it to machine.
Enabling Sentry on the project revealed that we’re way over the limits of their free tier. LLMs were most useful to fix that, suggested to create sentry skill and they will take care of that. Now /sentry ABC-123 makes it stop.
- Fixes it?
- Maybe.
- Applies strategic fix?
- Perhaps.
- Fixes the actual problem in the codebase, not the symptoms?
- Highlights it at best.
So this sentry skill became agentic workflow, although elders of this epoch would call it a cli and cron job. If you do that, yeah, I guess you mastered agentic workflowing. Problem shifted to never ending token consumign loop of the agent finishing the work while introducing more work for agent that checks sentry and fixes the issues. Those things are definitely not good for the planet, for mental health and are the opposite of perpetum mobile that we’re trying to invent this millenia.
Obviously the codebase needed full human-led reachitecture. Machine-led attempt ended in even more Golang SLOC. It went on for almost 12 days burnings baggillions of tokens and still was stuck so I had to interfere.
Machines allow to write anything you want with any speed you like for relatively small amounts of money. Humans control only one triangle of the classic Quick/Good/Cheap-pick-2: quality of the solution. Which converges on control of all other, but that’s for another blog entry.
Essentially I’ve bouth myself some time to think while machine was working: What’s the best way to change the codebase so that human could make changes to pretty complex system with LLM prompting?
Turns out the question is not novel at all:
- Aug 2025, Helio already prepared the blog post and presentation almost exactly one year ago.
- Symfony has even has Claude skill. Well, I too use symfony for this particular one and you didn’t come here to pick a skill, did you?
- Rust people loudly ported the markdown skill to rust as they usually do.
- Even Frenchmen use it in their Mistral Vibe.
- I mention only these because they are fun, you can look up metrics tons of recently generated code in this style.
So my idea goes to very simple primitives in handling the HTTP traffic:
HTTP controller
→ business service
→ another business service
→ action port
→ DBAL/mail/filesystem/etc action implementation
(think of any I/O)
A service represents one business use case. It coordinates authorization, applies business rules, owns the transaction boundary, and controls the order of writes, audit records, and other side effects. We removed the event system so a coding agent can open one service class and follow the entire operation from start to finish, almost as business prose. We use dependency injection to supply collaborators and create explicit seams where persistence, mail, file storage, and other subsystems can be replaced or mocked.
One could say that service layer in this case would become functionally pure, because it doesn’t cause any side effects, it’s just calls the actions for side effects. But I don’t want to anger FP gods.
The important dependency direction is inward: CreateTask service knows the InvoiceWorkflow service as well, but it does not know DBAL, HTTP, SQL schema, mail transport, just coordinates it. Like on the example:
$result = $this->transaction->run(function (): TaskCommentSaveResult {
// it's nice when business workflows generate the invoice
$preparedInvoice = $this->invoiceWorkflow->prepare(...);
// but also they generate tasks for employees
$taskId = $this->taskWriter->insertTask(...);
// Decides if we need to do IO
if (null !== $activity->preparedGeneratedInvoice) {
$persistedInvoice = $this->invoiceWorkflow->persist(
$activity->preparedGeneratedInvoice,
$occurredAt,
);
}
// Obviously writes
$written = $this->commentWriter->save(...);
// Writes some more
if (null !== $persistedInvoice) {
$invoiceSystemCommentId =
$this->invoiceWorkflow->recordComment(...);
}
return $result;
});
So the business logic is expressed in the single file, no events to jump with, human can essentially follow the procedures. Wikipedia calls it Hexagonal Architecture.
My take why it makes sense for the agents in very primitive zen statements:
- Agentic focus: agents makes the main changes as close to human operator as possible. Humans query in business terms and agent makes the business changes in the same terms.
- Mechanical operations stay out of the big picture, but can be zoomed in when needed. Agent doesn’t need to trace how data is retrieved from the database to give business answer.
Remember the idea that the person that develops the software doesn’t know anything to git? Seems like ports and adapters allow ephemenral project manager and machine talk very similar languages.
Cool beans, cover it with integration and unit tests, can it and you sell.
Intermezzo
The machine went working for next 5 days to rewrite the app again. That’s when symfony got in because it’s excellent implementation of DI helps make the code really readable for machine.
I have many other projects, so I tended to them next and explored this particular idea further. Again, agents bought me some time.
And another wonderful idea lit up. What if we sprinkle it all with BDD-style acceptance tests? Those may be run on many levels, throught many tools. Essentially every border of the Hexagon has to follow the same behavior in this application. BDD tests become applicable over any of the hexagon outer lines if you still follow the Hexagonal architecture. So essentiallly behaviour tests will check services and validate their business behaviour.
Suddenly those endless prompts to LLM can become deterministic (read cheap in time and money) verification procedures that you can run twice a minute for entire system.
So how does it help our ephemeral person that doesn’t know how to code with the work that they are doing?
Conversation with LLM changes dramatically because any statement can be codified in verifiable behavior and the operator can zoom in or out on these verifiable statements when building any further modifications. It’s difficult to review the pull request full of code and markup, but BDD tests are human-readable in most use cases. Every BDD library tries to make it as close to English as possible.
We get the human-readable statements that can be deterministically proven with application code that helps machine to keep the focus on one thing at a time.
A couple months from now we’ll see how it turns out.