When our team set out to build a third-party logistics (3PL) system for U.S. warehouse operations, we made Claude Code the main coding agent from day one and wanted to see how far a spec-driven workflow could take a project built from scratch. About 3 months later, the system is running in production, while a project of this size written by hand would normally take us around 6 months.
Most of that gain came from spec-driven development (SDD), where every task reached the agent together with the verified project context and clear rules for implementation and acceptance. The faster pace also changed the developer's role, moving attention toward architecture and review and leaving a shallower personal understanding of the generated code, a trade-off we look at closely later in the article.
Below, we share how we prepared the context and organized the work, how testing went, where the process broke down, and which practices we plan to keep on future projects.
| Project | 3PL system for U.S. warehouse operations, built from scratch |
| Main tools | Claude Code for implementation, a second AI model for independent review of documents and task breakdown |
| Approach | Spec-driven development with task cards stored in the repository |
| Timeline | About 3 months from architecture to production |
| Status | Launched in production |
What the 3PL system includes
The system covers the client’s work with the warehouse from the first stock delivery to invoicing and payment. It consists of 13 modules:
| Module | What it covers |
| Dashboard | An overview of the client’s account and current activity |
| Products | The client’s product catalog |
| Inbound Shipments | Stock deliveries sent to the warehouse |
| Inventory | Current stock levels in the warehouse |
| Product report | Reports on the client’s products |
| Orders | Customer orders fulfilled from the warehouse |
| Stores | Connected third-party stores |
| Import orders | Orders that arrive by API from other platforms, such as Shopify |
| Returns | Items sent back to the warehouse |
| Disposal | Disposal of stock the client no longer needs |
| Invoices | Invoices for warehouse services |
| Payments | Payment of invoices directly in the system |
| Balance | The client’s account balance |
Why we needed a different approach
AI code generation has become standard practice in software development. Few teams write every line by hand anymore, and a growing share of code comes from AI agents working under an engineer's direction. For us, the open question was how to use AI well on a large project.
A new system needs dozens of consistent decisions across modules that don't exist yet, such as the layer structure, data model, API format, and testing rules. An agent that sees only the current task makes choices that look reasonable locally and clash later.
Our main challenge was giving the agent a complete, stable picture of the system. Spec-driven development handled this: the project's rules live in files the agent reads at the start of every session, and its work doesn't depend on any earlier chat.
Building the context the agent needs
Before writing any code, we gathered the material the agent needed to understand the client's business and our technical standards. It came from three sources:
- Business requirements. The technical specification for the 3PL system, with the business logic and functional scenarios behind the warehouse processes it must support.
- Our proven components. Ready-made modules and packages we rely on for specific functions, from layering conventions to standard features.
- Project documentation. The agent had access to project sources, including Confluence documentation, which enriched its context with extra detail on requirements and decisions.
From these sources, we produced two core documents that every later agent session relies on:
- architecture.md: layers, classes, testing rules, API response format, and the overall project skeleton.
- spec_db.md: the complete database structure.
Each AI tool had a clear role:
| Tool | Role in the project |
| Claude Code | Studied our components and packages, then implemented the tasks |
| A second model with high reasoning (ChatGPT) | Helped align the documentation and checked the task breakdown against the specification |
Two days that set the direction
Initial architecture design and task planning took about 1 to 2 days. We clarified and manually reviewed every important output before moving on.
architecture.md
Working from the specification and our components, the agent described the layers, classes, testing rules, API response format, and overall skeleton. The first version contained a design mistake: it proposed a separate manager class for almost every controller method. We fixed this and other inaccuracies over several review iterations.
spec_db.md
In a separate chat, we built the complete database structure from the source materials and the approved architecture. The schema went through several generations and refinements before implementation started.
Large task cards
We split the full scope into about 24 to 25 domain or functional tasks, each with 5 to 7 subtasks. The first version was too detailed, so we revised the plan several times, adjusting the prompt and reworking the structure and content of the cards until we reached the final version.
We paid special attention to whether the tasks matched the specification. A second model with high reasoning reviewed the cards and subtasks independently.
By the end of this stage, we wrote down the architecture agreements, and the agent had a plan to follow for the rest of the project.
A task model that lives in the repository
We moved the plan straight into the repository. Backlog, active, and completed folders tracked the state of both large tasks and their subtasks. A large task card described the scope of work, while detailed subtask cards described how to implement it and how to accept the result.
tasks/
├── backlog/
├── active/
│ ├── domain-task/
│ │ ├── task-card.md
│ │ ├── backlog/
│ │ │ ├── 01-....md
│ │ │ └── 02-....md
│ │ ├── active/
│ │ └── completed/
│ └── quicktasks/
│ ├── fix-a.md
│ └── fix-b.md
└── completed/
The structure is illustrative, and file names are placeholders.
We worked with three types of cards:
| Card type | What it holds |
| Large task | A domain or major functional block. The card at its root describes the expected result and lists the planned subtasks. |
| Subtask | Created with a separate prompt. Has a defined scope, dependencies, implementation rules, and acceptance criteria. |
| Quick task | A small business-logic fix kept as a standalone card, with no decomposition. Moves to completed after acceptance. |
Each subtask card stated the goal and linked the relevant parts of architecture.md, spec_db.md, and the specification. Its acceptance criteria listed the required tests, API checks, and documentation.
A task changed state only after we verified its result in practice. This kept a long development process consistent and easy to resume between sessions.
The full cycle of one task
Every large task went through the same four steps, and each subtask was executed separately against its specification and acceptance criteria.
- Decomposition. The first prompt combined the task card, architecture.md, and spec_db.md and turned the large card into ready-to-run subtask cards.
- Implementation. Each subtask was moved to active in turn. The agent wrote the code along with unit and feature tests, and checked new API endpoints with cURL.
- The "-99" control step. A final subtask numbered "-99" ran an end-to-end check of the whole task and produced two reports.
- Review and completion. After checking the API contracts and business logic, we moved the task to completed and handed it over to the frontend team.

The "-99" step produced two artifacts:
- Frontend documentation: API contracts and expected responses, plus a description of the business logic in its final form.
- A cURL check log: concrete requests to the local environment, with payloads, HTTP responses, and results.
When the review found an issue, the fix followed the same path: a new subtask card, implementation, a repeat check, and updated documentation.
Checks focused mainly on API contracts and business scenarios. This kept the results reliable, but the developer's full understanding of the generated implementation became a separate risk, which we cover below.
Automated testing: where it helps and where it stops
Claude Code generated unit and feature tests on its own and ran HTTP checks with cURL. This cut manual API work, and in many scenarios we needed Postman far less. The cost showed up later, when the growing test suite had to run.
We ran checks at three levels:
| Level | What runs |
| Subtask | Relevant automated tests and API requests |
| Task ("-99" step) | A final end-to-end check and the cURL report |
| Full regression | The complete test suite |
Full regression became the bottleneck, with a complete run taking several hours.
Incidents along the way
- Disk overflow. Tests generated so many PDF files that the disk filled up and Claude Code stopped.
- Local database loss. During some test runs, the agent deleted the local database.
- Agent conflicts. Several agents running in parallel interfered with a shared environment and got in each other's way.
We found no serious errors in the business logic itself. The most visible failures involved the test infrastructure and the way agents interacted with shared state.
Why specifications are critical
An agent is limited by its working context and doesn't retain a full understanding of the system between sessions. Documentation became our way to restore the project's intent without rereading the whole codebase each time. However, outdated specifications can do more harm than having none at all.
Small early mistakes get expensive
At the start, the difference between two decisions looks minimal. Once dependent modules are built on top, fixing a field type or a layer boundary can require significant rework. Two practices helped:
- Keep specs in sync with the code. Any client change that touches the structure or business rules updates architecture.md and spec_db.md together with the code.
- Record why decisions were made. Explanatory comments in the code can capture why a non-obvious solution was chosen and which mistake future agent sessions should avoid. We haven't measured the effect yet.
Technical debt to watch
Even with the early design mistake fixed, the manager layer grew too large. It is a candidate for architectural review and possible refactoring.
Models and the limits of autonomy
The whole approach ran on standard prompts and file-based specifications, with no custom tooling.
| Period | Main mode | How it was used |
| First 2 months | Sonnet, medium reasoning | Main implementation. Opus handled decomposition, complex decisions, and periodic code analysis. |
| Last month | Sonnet, low reasoning | Main code generation in a more economical mode to stay within weekly usage limits. |
A clear line for Git and the server
| The agent | The developer |
| Read the Git state | Created branches |
| Generated and changed code | Committed and pushed to the repository |
| Ran local checks | Connected to the server and ran git pull |
The agent was not allowed to perform any Git action that changes state. Deployment and repository management stayed fully manual.
Results
All core features of the 3PL system are live in production. The project took about 3 months from architecture to release. Based on our experience with similar systems, building it by hand would have taken about 6 months, so the agentic approach cut development time roughly in half.
Large tasks also moved much faster:
| Scenario | Typical duration of a complex task |
| Before the agentic approach | A week or more |
| 3PL project with Claude Code | 1 to 3 days |
Most of the effort shifted from writing code to shaping the project: designing the architecture, refining specifications, preparing task cards, and reviewing results.
Where the approach worked best
- New projects: maximum effect. A system built from scratch, with the architecture and database described up front and large tasks broken into subtasks.
- Existing projects: a more modest effect. Fixes and small features based on standalone spec cards. The agent studied the code while working, without a new set of architecture documents.
These figures come from one project and our team's own assessment, without a controlled benchmark.
The trade-off: faster delivery, shallower understanding
Faster development came with a cost that matters more than any timeline figure: the developer understood the code less deeply.
The developer stayed in control the whole time, from writing prompts to accepting each task. Reviewing code, however, works differently from writing it. Writing code yourself builds cause-and-effect links in your head, such as why a class exists and what breaks if it changes. A review leaves a more surface-level familiarity, and some details or connections can slip past.
A developer who reads generated classes understands each piece but may struggle to explain every link and the full execution path. We treat this loss of end-to-end code ownership as a real risk and keep it in check with regular walkthroughs and architectural reviews.
Practical rules for agentic development
These rules sum up what we would carry into the next agentic project. Most of them concern the work around code generation, where this project's quality was decided.
- Design the system carefully. Documentation, architecture, the database schema, and task cards all need a thorough review. A mistake made at the start costs far more after dozens of tasks are built on it.
- Control tests and split test runs. Check which tests the agent creates. Use fast smoke tests and targeted checks for frequent runs, and schedule deep verification as a separate, less frequent run.
- Spot the need for refactoring early. Review generated code and layer boundaries regularly. A weak decision is easier to fix right away than after ten related tasks.
- Keep your understanding of critical code. After each major stage, walk through the key scenarios and keep the specifications in sync with every change.
- Protect the test environment. Isolate the test database and limit the files tests create. Give parallel agents separate environments to keep checks from damaging shared state.
Conclusion
The quality of agentic development depends first of all on decisions made before any code is generated. On this project, the agentic approach cut delivery time from an estimated 6 months to about 3 and helped us launch a production 3PL system. The price was a shallower personal understanding of the code, which we now manage deliberately.
Design carries the most responsibility. Current specifications and regular code review let a team use the agent's speed without piling up mistakes that become expensive to fix later.
Read more articles in our blog