The first time I used Kiro was at the AWS Summit in London earlier this year. It was a hands-on workshop, so on the rails run through to show what the tool could do. Everyone was converging on the same idea at that point, that the spec was what mattered, and it all felt a bit contrived from Kiro just formalising it.

Attendees in the Kiro workshop at AWS Summit London 2026, seated with headphones while the presenters introduce the session

The first time I used Kiro was in a guided workshop at AWS Summit London.

A recent hackathon was a good excuse to go back to it, Ready, Spec, Ship, sponsored by Kiro: build a real application and let the judges run it. I didn’t have a project I was itching to make, but once I started thinking about what to make I landed on something I’d have wanted myself. That has been my guiding principle for my grand total of two hackathons so far. When I first put a website together earlier this year, I had no idea what good looked like, and what using a certain tech stack would actually mean for the website. So I built Surfdeck, a StumbleUpon (early 2000’s website) for the indie web with a provenance layer, around 350 curated sites, so you can “stumble” through real projects and see what they’re actually made of. It’s a Cloudflare Worker with D1 and a React front end, live at surfdeck.kashifnazir.com.

Everyone wants to credit the model, and the model is fine, but it isn’t what made this build work. It was the setup, tasks kept tiny without being starved of context, and that split is worth trying whatever you’re building.

Why the IDE and not the CLI

Kiro has a CLI now, generally available since November 2025, and I didn’t use it. This was my first proper build in the tool, so I chose the IDE to watch it work, seeing the diff review and the hook badges firing live.

The bit that matters is that the CLI runs the same agent off the same .kiro/ config, so everything below transfers whichever way you get at it.

The setup, piece by piece

Everything that made this work lives under .kiro/, and it comes to four things:

├── steering/                 # standing project knowledge
│   ├── product.md            # 3 auto-generated
│   ├── tech.md
│   ├── structure.md
│   ├── design.md             # + 2 I added
│   └── taxonomy.md
├── specs/                    # one folder per feature (×7)
│   └── feature-name/
│       ├── requirements.md   # EARS notation
│       ├── design.md         # API contracts
│       ├── tasks.md          # numbered plan
│       └── tasks.meta.json   # test tracker
├── hooks/                    # fire on file save
│   ├── validate-data.json
│   └── run-tests.json
└── settings/
    └── mcp.json              # one MCP server

What I leaned on hardest was the steering. These are Markdown files Kiro keeps in context as standing project knowledge, so I wasn’t re-explaining my conventions every prompt. The part that earned its keep was the inclusion mode: front-matter decides when each file loads, and I set the taxonomy one to load only when matching files were open. So that block of vocabulary was there for the three cycles that touched it, and gone, and free, for the other four.

The specs are where a feature idea turns into requirements, a design, and a task list, with each task pointing back at the requirement it satisfies. I read the requirements and design Kiro produced before building on them, the same reason I kept Autopilot off later: I don’t take an agent’s first output on trust. I ran seven, one per narrative thread, and kept each task down to one logical unit. Task 5.1 was the one API route, not the whole API. What I didn’t expect to matter as much as it did was the tracker: mine logged the property-based tests as they passed, around twenty-one covering the seed and stumble engine, so later tasks weren’t re-deriving what was already proven.

Kiro IDE showing the MVP Stumble task list beside the agent completing task 1.2 and validating the D1 schema

Small tasks worked because the standing and proven context was already there when each one started.

That is the whole low-token idea, and it’s the reason the tasks stayed surgical. I never pasted the project into a prompt, because the standing knowledge was already in the steering and the proven knowledge was already in the tracker. Kiro meters credits by usage, and so keeping each prompt small but well-supported was directly cheaper.

The two hooks both fired on file save, one checking the seed data whenever the CSV changed and one running the tests on any source edit. Having them fire live meant the agent got told it had broken something before it declared the task done. The MCP was the one piece I added to extend the agent’s reach, Playwright, because it could render a live JavaScript site and screenshot it, which the built-in fetch can’t. That let it vet candidate sites and check its own UI against the running app. I obviously didn’t add a second one that just duplicated something already there.

What I got wrong

I wanted a gate at every step, so I ran with Autopilot off. The agent stopped and waited after each task, and I’d read the diff and run the tests myself before committing. That was the plan, and mostly I held to it.

Except for one cycle, where Kiro ran start to finish on Autopilot, passed every automated gate, and shipped the wrong design. Everything was green and the result was still wrong. It’s easy to write that up as the tool failing, but the tool did what I let it do. I should have stopped it to walk through spec, then design, then tasks, approving each before it built, the way I did everywhere else. That time I didn’t tell it to stop, so it skipped the same spec-and-design read I’d held everywhere else, and I didn’t notice.

What caught it was the review, because I’m not a coder and I’d had a second agent, Claude Code, checking Kiro’s work from outside the whole time. Some of that was cost, since I already had Claude and didn’t want to spend Kiro credits re-checking, but mostly an adversarial check is good practice and I want one whether or not I’m paying for it. That second agent, or Kiro, I can’t tell you which, is also what surfaced the worst bug of the build. A design decision that used a temp table passed the type checker and the local tests, then fell over at runtime, because Cloudflare’s D1 doesn’t allow temp tables. It’s a permission error you only see on the real engine, and in my case it cost a design pivot mid-build.

What I’d do differently, and what’s next

Having built Surfdeck with a second agent watching, I’m not sure I needed one. Everything Claude Code did from outside, Kiro can do in the loop: the hooks run the tests, the MCP checks the UI, and supervised execution gates the rest. If that’s all you’ve got, it’s most of the same safety net inside one tool. I’ll still run the second agent, because I like the adversarial check, but that’s a preference now.

Next time I’d try the CLI for the scriptable parts, the headless test and deploy gates that belong in CI where the IDE can’t go, and keep the IDE for authoring specs and reading diffs. The config carries over untouched, and so there’s nothing to migrate. Kiro has also since shipped a multi-agent layer, Kiro Crew, that runs on the CLI and reads the same setup, which is the idea I already liked scaled up. I haven’t touched it yet so looking forward to giving it a try soon.

Surfdeck self-portrait listing seven Kiro specs, two agent hooks, 404 tests, more than 82 process-log entries, and the Cloudflare, D1, React, and Hono stack

Seven specs, two hooks, and a human gate at every stage—the setup behind the finished Surfdeck.

The model matters, but the model isn’t what made this build feel different from that workshop back in April. It was that the tasks stayed small and cheap, with the context they needed in the steering docs and proven in the tracker, so it never had to be re-pasted every prompt. The code is on GitHub at github.com/thekashifnazir/surfdeck if you want to see how it fits together, and Surfdeck is live if you’d rather just stumble through it.