RSSAmplifier

javatask.dev · Apr 29, 2026

Spec-Driven Development with AWS Kiro

0
Sign in to vote or save

Andrii Melashchenko · javatask.dev

Three sprints into a new backend service, you open the project in Kiro and watch it propose changes that contradict a decision you made in week one — because that decision lives in Slack, not in a file the agent can read. The spec-driven workflow exists to close that gap.

That scenario is not a Kiro failure. It is what every AI coding assistant does when the project’s institutional knowledge lives outside the filesystem. The agent did not forget — it never had access in the first place. Spec-driven development is the practice of giving it access: structuring your requirements, design decisions, and task breakdown into files the agent reads as a first-class input at every session. The change is architectural, not cosmetic. What the agent can reliably deliver is bounded by what it can reliably read.

This post walks through the spec-driven loop as implemented in AWS Kiro CLI: requirements.mddesign.mdtasks.md → grind. It then covers the two supporting mechanisms that make the loop durable across sessions — steering files as persistent project context, and agent skills as on-demand expertise. Part 2 (Orchestrating Agent Teams in Production, 2026-06-05) extends the picture to multi-agent workflows.

Side-by-side comparison: vibe coding produces a fading chat history; spec-driven development produces a four-file loop with arrows looping back.

Figure 1 — chat history vs the four-file spec loop. What survives the session is what the agent can read at the start of the next one.


The four-file spec loop#

The Kiro FAQ frames this directly: it keeps the experimentation of “vibe coding” but fixes some of its limitations. The structural fix is the spec-driven workflow, organized around a .kiro/specs/ directory inside your project.

A spec represents a bounded feature or change. The loop has four stages.

requirements.md#

This file answers: what does the system need to do, and why? The Kiro approach encourages prompting the agent to ask clarifying questions one at a time rather than front-loading every constraint in a single prompt. The agent works through the requirements incrementally, producing a requirements.md that captures the functional scope, the acceptance criteria, and the constraints that are in scope.

What makes this file load-bearing is the discipline of writing it before any code runs. It is not a summary of what was built — it is the contract that precedes building. When you reopen the project two weeks later, the agent reads requirements.md first. The Slack decision from week one now has a file-system home.

A thin requirements.md produces a thin design.md. This is where most teams underinvest. The agent can only make decisions consistent with the constraints it has been given. Incomplete requirements do not produce incorrect agents — they produce agents that make locally reasonable choices that are globally inconsistent with what the team intended.

design.md#

The agent generates design.md from requirements.md. This file covers the technical architecture: data structures, component boundaries, external dependencies, and what the official Kiro docs describe as “correctness properties” — the behavioral invariants the implementation must preserve.

Your job at this stage is to review design.md before approving it. Kiro generates a plausible design given what it knows; it cannot generate a correct design if it does not know your team’s constraints on, for example, which AWS services are approved, which external libraries are pinned by your compliance team, or which interfaces cannot change because they are shared with a downstream system. Reviewing design.md is not a formality — it is the point where your architecture judgment enters the loop. The ten minutes spent correcting a design assumption here avoid the three hours of refactoring it downstream.

tasks.md#

From design.md, the agent produces a tasks.md: an ordered, numbered list of concrete implementation steps. Each task in the list is scoped to a size the agent can execute in a single action — a file to create, a function to implement, a test to write.

This is the document that turns the spec into executable work. The agent uses tasks.md as its working memory during the grind phase. When a task completes, it marks it done and moves to the next. When the session ends and you return the following morning, the agent picks up from the first uncompleted task. The spec survives session boundaries because tasks.md does.

One discipline worth establishing early: do not modify tasks.md by hand mid-grind unless you are explicitly replanning. Handwritten edits to a partially-executed task list create inconsistency between what the agent believes has been done and what has actually been done. If the scope changes, reopen requirements.md, update it, and let the agent regenerate design.md and tasks.md from the new state.

The grind#

Once tasks.md is approved, the agent executes it. This is the “grind” — sequential task execution against the spec, with the agent writing code, running tests, and iterating until the task’s acceptance criteria pass. The loop is: implement → test → fix → mark done → next task.

The agent operates within the tool permissions you have granted it. Tasks that require shell access, file writes, or external API calls go through the tool trust model — you define which tools can run unattended and which require explicit confirmation. For a first project with Kiro, the safe default is to keep allowedTools narrow and observe which permissions the agent actually needs before broadening them.

The .kiro/specs/<feature-name>/ directory contains all three files for a feature. After the grind, those files are the artifact you keep.


Steering as the persistent contract#

The spec loop answers “what are we building right now.” Steering files answer “what are we always building” — the standing context that every Kiro session begins with.

Steering files live in .kiro/steering/ and are read at the start of each session. The official Kiro Steering documentation specifies three foundational files: product.md, tech.md, and structure.md. I call this the Steering Triumvirate in the companion Blueprint — that label is my shorthand, not a Kiro term.

product.md defines the business context: what problem the software solves, who uses it, and what “correct” means from the product’s perspective. For an industrial data pipeline, this includes the downstream consumers, the latency expectations, and the failure modes that are acceptable versus those that are not.

tech.md captures the technology constraints: the runtime versions, the approved libraries, the cloud service choices, the compiler toolchain if embedded firmware is involved. Every constraint you document here is a decision the agent will not have to invent. Every constraint you omit is a decision the agent will make without you.

structure.md documents the project layout: the folder conventions, the naming patterns, the architectural layers and what belongs in each. Without this file, the agent applies a reasonable generic structure. With it, the agent applies your structure.

The practitioner guidance — and this is community-sourced observation rather than a Kiro spec requirement — is that steering files should be written for the AI, not for humans. A good steering file is specific, uses short declarative sentences, and includes concrete positive and negative examples rather than abstract principles. “Always use uint32_t instead of int for memory offsets to ensure 32-bit compatibility” is a steering rule. “Follow good coding practices” is not.

On length: steering files cap at roughly 200 lines before they become noise rather than signal. A bloated tech.md that exhaustively catalogs every library is less useful than a focused one that captures the five decisions most likely to trip up an agent. The 200-line heuristic is from my own experience on the 50K-line embedded C project that anchors this series — your number may differ, but the principle holds: steering files activate on every session, so every line that is not load-bearing is overhead.

One trap the Blueprint calls out explicitly: treating product.md as a product roadmap rather than a product definition. If it lists planned features, the agent may treat future scope as current scope. The file should define what the software is, not what it will become.


Skills as on-demand expertise#

Steering files are always-on. Agent skills are on-demand: they activate when a request semantically matches the skill’s description, and they are otherwise absent from the context window.

A skill is a directory containing a SKILL.md file and optional supporting assets — reference documentation, scripts, templates. The SKILL.md uses a two-field YAML frontmatter block. The official Kiro Skills documentation specifies: name (max 64 characters, lowercase alphanumeric plus hyphens) and description (max 1,024 characters).

The description field is the semantic trigger. When the agent receives a request, it matches the intent against the descriptions of all available skills. A skill with description: "Review pull requests for code quality and security vulnerabilities" activates when you ask Kiro to look at your latest changes — no manual invocation required. The agent loads the full skill content into its active context when the match fires.

This mechanism — automatic activation by intent — is what makes skills a structured alternative to bloating the system prompt or repeating instructions in every session. The constraint to understand is that the description field is doing two jobs simultaneously: it is the semantic key that triggers activation, and it is also a cost driver, because the skill’s instructions load into the context window on every activation. A verbose skill with detailed instructions is fine for infrequent, high-expertise invocations. A verbose skill that activates on every minor code question is a persistent tax on context and token budget.

The practical authoring guidance follows from this: write the description field to be specific enough to trigger on the right requests and specific enough to not trigger on unrelated ones. A skill named embedded-hal-reviewer with description “Review Zephyr RTOS driver code for HAL abstraction compliance and memory safety” will fire when it should and stay quiet when you are asking about Python test coverage. A description of “Help with code” fires on everything and helps with nothing.

Skills are defined by an emerging open specification hosted at agentskills.io, with current adoption in both Kiro CLI and Gemini CLI. The spec is real; the ecosystem is early-stage.


A worked example: backend service with a data-pipeline skill#

Consider a backend service that ingests sensor readings and writes them to an S3-backed data store. The team has three recurring concerns: schema validation rules for the incoming payload, the retry and dead-letter queue configuration for the SQS consumer, and the partitioning convention for the S3 prefix structure.

The steering setup: product.md defines the sensor ingestion problem and downstream consumers; tech.md pins the Python version, the AWS SDK version, and the rule that all IAM roles use least-privilege inline policies rather than managed policies; structure.md defines the module layout and the convention that each module exports a single typed interface.

The team also has a payload-validator skill whose description covers schema validation for sensor data. When the team asks Kiro to review a new message handler, the skill activates and brings the validation rules into context automatically.

A feature spec for “add temperature sensor support” lives at .kiro/specs/temperature-sensor/. The requirements.md captures that temperature readings carry a unit field (celsius or fahrenheit), that the validation layer must reject readings outside the -50 to 150 range for Celsius, and that downstream analytics requires normalized Celsius in S3 output. The design.md captures where normalization lives (in validation/, before the S3 write). The tasks.md breaks this into: implement the unit-aware validation function, add normalization, extend the S3 writer, write integration tests, and update the dead-letter queue handler.

The agent grinds through that task list against the steering files. It does not propose a managed policy for the new IAM role because tech.md says not to. It does not deviate from the module structure because structure.md defines it. The Slack decision from week one is now a steering rule the agent reads on every invocation.

If your stack is embedded firmware rather than a backend service, the AWS samples repository sample-sdlc-using-kiro-and-zephyr-on-edh provisions a Graviton-backed development environment with Kiro pre-installed. Note that the sample (which uses “EDH” as its own label for the provisioned environment, not a named AWS service) defaults to a t4g.large EC2 instance — t4g is burstable, and sustained QEMU emulation builds exhaust CPU credits within minutes. For sustained emulation workloads, c7g.large (compute-optimized Graviton3) is the right sizing choice. The sample is a useful starting point for embedded teams; just budget the instance class correctly before running long builds.


The audit trail you keep#

After a feature is complete, the .kiro/specs/<feature-name>/ directory contains three files: the requirements that defined the scope, the design that defined the approach, and the task list that governed the execution. Together, they are a bounded record of a decision.

This is the artifact worth keeping. Not the chat history — chat histories are ephemeral, unstructured, and unsearchable. The spec files are structured and version-controlled. When a new team member joins and asks why the temperature normalization step lives in validation/ rather than storage/, the answer is in design.md. When a bug is reported six months later and the question is whether the retry behavior was intentional, the answer is in requirements.md. When someone from a different team asks what the payload validation rules are, the answer is in requirements.md and can be linked directly.

Two-column comparison: a fading chat-history scroll on the left, a structured .kiro/specs/ directory tree on the right with annotations on the load-bearing files.

Figure 2 — chat history is ephemeral; the .kiro/specs/ directory is the engineering record you commit alongside code.

Treating .kiro/specs/ as a first-class repository artifact — committed alongside code, reviewed in pull requests, referenced in commit messages — transforms the spec loop from a productivity pattern into an engineering record. Teams that discard spec files after the feature ships leave the most durable value on the table.

The spec files also serve a handover function. A junior engineer inheriting a module with a populated .kiro/specs/ directory has immediate access to the decision history that a senior engineer built up over months. That is a basic property of structured documentation enforced by the workflow rather than by convention.

One observation worth keeping from the Blueprint: the spec directory after a session reveals the quality of the session itself. A thin requirements.md paired with a dense tasks.md suggests requirements were underspecified and the agent improvised. Treating the spec review as part of a PR review creates a lightweight feedback loop on spec quality that compounds over time.


What to measure on your own stack#

On the 50K-line embedded C project that anchors the companion Blueprint, the time from “I need to understand this subsystem” to “I have a defensible refactor plan” dropped from two days to two hours under Kiro’s tiered context model. That is a single project, a specific team, and a specific domain. Your codebase, your team’s spec discipline, and your steering file quality will produce different numbers.

What to measure: the time from receiving a feature request to having an approved requirements.md. The number of spec revisions before tasks.md passes review. The frequency of grind-phase interruptions due to missing steering context. These are proxy signals for spec quality, not benchmarks for claiming productivity percentages.

The Kiro FAQ’s framing — that spec-driven development “fixes some of the limitations” of vibe coding — understates the architectural shift. What actually changes is the persistence and retrievability of context. An agent that reads requirements.md, tech.md, and structure.md at the start of every session is not smarter than one that starts from scratch. It is operating in a structured environment that encodes the team’s decisions in a form that survives session turnover, team turnover, and time. That is worth maintaining.


Part 2 of this series — Orchestrating Agent Teams in Production (2026-06-05) — examines what happens when you compose multiple Kiro agents: fan-out/fan-in dispatch, isolated context as an architectural property, and the consolidation cost that every multi-agent design must budget for. The full series reading path, including the 14-page AWS Kiro Engineering Blueprint, is at /series/agentic-ai-on-aws/.

Read the original on javatask.dev

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.