Agentic Engineering Under Constraints
At work, I do not have access to the kind of fully enabled agentic setup often shown in demos or that I use on personal projects. We use GitHub Copilot with approved models, organisation-level restrictions, limited agent capabilities, and a small strict budget. Some tools and all Model Context Protocol (MCP) integrations are unavailable.
That constraint forced me to build a more deliberate workflow.
That shift led me towards bringing the agent in with a specific, scoped purpose. I have to be cautious not to let the agent burn through the budget before I can complete the task.
The workflow at a glance
The process looks roughly like this:
- Ideate until the ambiguity is explicit and there is a shared understanding
- Capture the requirement as a human-readable handoff document
- Break the work into vertical, testable slices
- Start a fresh execution session
- Use an orchestrator/sub-agent pattern where possible
- Verify with tests, linting, documentation, and review
- Commit at meaningful checkpoints
Loading graph... Javascript is needed for the graph to render
Ideation: spend effort before code
Ideation, a term I’ve picked up from product and UX, helps me flesh out the initial intent. Where a skill like Matt Pocock’s “grill-me” skill challenges assumptions of a feature change, my intention is to work through ambiguity to align those assumptions with reality.
Ideation is rapid iteration and feedback. It is Q&A, codebase exploration, documentation review, and assumption testing.
Navigating through the initial ambiguity, the session focuses on turning the vague intent into something actionable. The only way to do that is to surface and solidify decisions, as remaining vague during this process creates opportunities for wasted effort and significant divergence later on.
Once the decision-making, edge-case capture, and evidence-based research are complete, the context is now full of really good, well-rounded information that can easily get lost if not acted upon correctly. What’s next depends on the size of the scope. If the workload itself is relatively small and concise, it might be possible to immediately act and move on to implementation.
Creating artefacts to hand off to either engineers or agents is critical for stateful progression through this complex piece of work. Because MCPs are disabled, I can’t instruct the agent to create Jira tasks or tickets. Therefore, I rely on the file system to create markdown files within source control.
Handoff: the implicit made explicit
So that I don’t lose the value that is now in the context, I create this handoff artefact. It’s intended to capture the feature, the product and behaviour requirements, but importantly, no technical implementation details.
I put guardrails around this artefact to ensure that it’s readable and understandable by everyone in the process, from product managers, engineers, and agents. By avoiding implementation details, I keep the document durable. It captures intent in a way that can still make sense later, even if the code changes or the original approach becomes irrelevant. When working with this, I briefly proofread the artefact to ensure it aligns with what has been captured. There is still a possibility for deviations and hallucinations. If not spotted early enough, they can propagate through the task and implementations later.
This somewhat deviates from spec-driven development, where the specification is a living document representing the application. This is a snapshot of the intent and realised requirements to achieve the outcome. It allows for more flexibility in execution and the ability to hand off work is especially useful for remote workers and asynchronous workflows.
Creating this artefact is the first major checkpoint that allows visibility to those not in the session. At this point, stakeholders can align quickly around an idea that has moved from vague possibility to something grounded in reality. On this recent piece of work, these artefacts gave reviewers valuable insight into the work and helped them clearly understand the bounds of implementation.
Slicing for delivery, not busyness
Slicing work into manageable chunks is a common challenge, and mature engineering teams will have their own established processes for this. However, agents lack this structured approach and, given their autonomy, must rely on direct guidance and preferred methodologies.
Given no external constraints or dependencies, I focus on vertical slices that are observable and testable with full integration. The difference is that each task needs enough meaning to constrain the implementation. Small, isolated tasks often give the agent too much room to invent structure that the feature does not need.
A poor task would be “Create a DB dao”. A better task would be “Support this user-visible flow end-to-end”. The latter gives the agent a real outcome to work towards and grounds the work in existing patterns and operations.
Ensuring that these vertical slices have meaningful deliverable outcomes anchors the agent to work within the constraints given. When breaking requirements into tasks, I ensure that decisions not clearly expressed in code are captured in documentation. Having a strict rule for this prevents over-documentation and unnecessary comment blocks, while ensuring that the reasoning is explicit.
When I was initially setting up the proof of concept for this piece of work, the handoff document was ambitious and captured many edge cases. From that, eight distinct tasks were created, each encapsulating a single scope of work. These tasks included blocking relationships. The more distinct the scope is per task, the more the tasks can be executed in parallel.
Orchestrate, verify, and checkpoint
Once the handoff artefacts exist, I start a fresh session. That clears out the noise from ideation and gives the execution phase a narrower focus: implement the tasks, verify the result, and checkpoint progress.
In this pattern, the orchestration agent keeps hold of the broader requirements and coordinates task completion. The sub-agent focuses on implementation: inspecting files, editing code, updating tests, and making targeted changes across the repository.
The orchestration agent is for task delegation and verification once a task reaches completion. A separate agent will be executing the tasks. These sub-agents will be making tool calls to inspect, edit, and update files across the repository. They will need the technical know-how, while the orchestration agent requires domain expertise and cohesive understanding of the requirements.
I make sure the tasks encompass more than code. The agent that picks up the task is responsible for both code completion and decision documentation. Once the code lands, the decision becomes real. If the reasoning is not obvious from the code, I capture it in an ADR. And so, if there is any ambiguity about why an implementation has occurred, an ADR is created to clarify the purpose.
By using an orchestration agent, the context provided to these sub-agents is delegated with precision and with just the right amount of wider information, especially helpful when they are are able to work parallel. It’s also important to provide sub-agents with the correct tools to navigate codebases and documentation. Access to CLI tools like RipGrep and language server protocols (LSPs) help to provide efficient native navigation and understanding.
Once the task is complete, the orchestrator will help to verify not only that tests and formatting are correct, but also that the files changed are meaningful to the wider context. And when the task is completed successfully, I use Git commits as meaningful checkpoints. This allowed for normal source-control workflows and an auditable history of changes.
This pattern worked well for me in the piece of work where a sub-agent had removed a section of documentation by mistake and the orchestrator was able to identify the mistake and restore it. I assume that it was because the agent’s task had no relation to what was removed, and so saw no evidence of those statements. These evaluation moments help to reduce hallucinations or code smells and it’s not the only review that is ingrained in this process.
Engineers are still accountable
I’ve seen several tech talks and demos that claim little code is being reviewed, or that these artefacts for handing off aren’t being examined before reaching production. I disagree with the idea that agent-authored work should move to production with minimal review. I do not treat generated code as automatically bad, but I also do not treat it as automatically trustworthy.
Anyone who has worked with me for the last three to four months knows that I am actively reviewing or at least participating in every PR my team produces that is heading to production. This also includes documentation, ADRs, RFCs, and other related materials for our roadmap.
While these agents have become more sophisticated and better at producing code, the agent may not know every team convention, past decisions, or undocumented exceptions to those rules. And this is where I treat this process as a moment for reflection and review. I review it like work from someone who understands the syntax but does not yet understand the team, the domain, or the history behind our decisions.
Where a pull request of the past would be the responsibility of both the author and the reviewer, agents don’t carry that responsibility. It becomes even more paramount to exercise caution and critical thinking. Not to check whether it was coded exactly the way I would have written it, but to ensure that it’s following normal conventions and best practices.
Feedback loops enforce the explicit
Over the past eighteen months, the industry has shown both the benefits and consequences of adopting AI into the workspace. Teams that embraced AI-assisted coding early on have shown a decline in quality and uptime, impacting their company reputations.
Tests and linting give agents useful correction signals that can be used to stay on track with conventions. Rust’s clippy has a stellar reputation and is a shining example of this. The errors these tools produce are not noise. They are context. Feedback loops provide context about coding patterns, conventions, and expected business behaviours.
What cannot be expressed in code, but remains important to the project, is captured in ADRs. This ensures that a chain of decisions is followed and maintained. Where these documents exist, orchestrator and sub-agents alike should treat them as immutable and as an authority.
All of these puzzle pieces put together demonstrate a confinement that agents can navigate and implement within to keep code quality high, while retaining the ability for future contributions to be authored by either engineers or agents.
Where approved tooling could make this smoother
Working in a restricted environment forced useful discipline: clearer intent, more deliberate context, and more careful review. I would keep those habits even with more capable tooling.
However, approved agent capabilities could make the workflow smoother without removing the guardrails.
A lot of agent failure comes from missing conventions, undocumented patterns, or stale assumptions about how a codebase works. Giving the agent safe access to the right context would reduce the amount of manual hand-holding needed during execution.
The biggest improvements would come from:
- Safer multi-repository context, so agents can understand existing patterns more reliably
- Sandboxed execution, so agents can iterate without touching sensitive systems
- Cloud-based task orchestration support, especially when independent vertical slices can progress in parallel
- Integration with internal documentation, ADRs, and ticketing systems, so context does not need to be copied manually
What changed for me as an engineer
With this workflow, I can deliver code more quickly. But the time I spend on code is not reflective of total time to delivery. I spend a lot less time typing code, and a lot more time shaping the intent of what I am hoping to achieve.
Before code is produced, it is still the engineer’s responsibility to understand the task at hand. The outcome and the impact of their work and the ability to shape the intention from a requirement. This AI workflow emphasises the importance of upfront decisions in the production of code, recognizing that the process is quick. The focus is on determining whether the current implementation is the correct or best approach, given the knowledge available today.
On a given day, this might take up the first half of my day. Anecdotally, this changes how I spend the other half of my time. The second half of the day is spent on reviewing whether the code is correct, whether there are any additional concerns, and whether any edge cases have been missed. I will apply this same critical thinking whether the code was authored by an agent or one of my team’s engineers.
The value I provide has shifted from raw implementation speed to judgement, sequencing, accumulated context, and orchestration. There will always be implicit decisions and lessons learned through experience that AI agents do not have in their context. I also spend more time deciding what should not be built. Faster implementation makes poor sequencing more expensive, not less. The earlier I catch unnecessary scope, the less time the agent wastes producing technically correct work that should not exist.
Key takeaways
- Start with an ambitious goal, then reduce ambiguity early.
- Use handoff documents as shared context, not throwaway prompts.
- Keep implementations scoped so the agent does not invent unnecessary abstractions.
- Slice work vertically so each task delivers something observable and testable.
- Let the codebase answer where it can through tests, linting, examples, and existing conventions.
- Treat the human as the decision gate, not just the final reviewer.
- Capture decisions as you go, especially when they are not obvious from the code.
- Make implicit knowledge explicit. If the agent cannot see it in code, tests, documentation, or ADRs, it effectively does not exist.