
Kiro in a Real Project: Things I Wish I Knew Before I Started
How to turn your Kiro into a Lean, Mean Feature-Building Machine
I use Kiro every day to build an agentic system for a startup.
When I started, I did not spend much time researching how to best set up Kiro for the job: I read enough of the documentation to get going, created some steering files, picked the strongest model available, and started building.
It worked - features got built.
But my Kiro setup was nowhere near as efficient as it could have been. My context was bloated with way too much information in it, which was not needed for every feature, requirement document, design document, tasks or coding session.
- Eventually, I ran out of Kiro credits before the project was finished.
- Some operations took far longer than they should have.
- Kiro updated files I did not intend it to change, files that were not tracked by Git, so I did not immediately realise it.
- Security issues slipped in that should have been caught or prevented earlier.
- And as the system evolved, some of my vast amount of carefully written steering became outdated and started giving the agent the wrong context. Everyone knows – there is nothing worse than outdated documentation.
Today my Kiro setup looks very different from the one I started with. These are the things I wish I had known before I started.
Set up .kiroignore before you start.
For a while I thought Kiro had a bug. Every time I did a refactor with Kiro whereby Kiro would rename classes or move packages around, my workspace would appear to get corrupted. The Gradle build would not run anymore, and git commands would take a very long time or timeout.
Quite a few times I was forced to delete my project locally and check out the remote version again to get things working again. Eventually, I realised that it was not a bug; Kiro was simply being too thorough. When it renamed or moved something, it also updated references in places it should not have touched, including files inside
.git/, and Gradle caches.Those files belong to Git and the build tool, they are not meant to be updated by me nor the agent.
The fix was a
.kiroignore file. Do not stop at the examples from the documentation, which mainly put files with security credentials into .kiroignore. Add the internals and caches that are specific to your stack as well.This is what I use in this project:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Gradle cache and build artifacts
.gradle/
build/
.kotlin/
# Git internals
.git/
# IDE and editor files
.vscode/
.idea/
# Other common exclusions
*.class
*.jar
*.war
*.logChange it to match your stack:
1
2
3
4
node_modules/
dist/
.venv/
target/Ensure you do this at the start; this saves your context from unneeded files and prevents Kiro from changing files outside its scope.
My first steering files were far too ambitious.
When I first set up steering, I tried to give Kiro as much context as possible.
That sounded sensible.
My steering documents contained product positioning, competitor comparisons, long-term product ideas, and other background that might have been useful while brainstorming.
Most of it was useless while implementing a feature though.
Worse, it was being pulled into requests where it added context cost without helping Kiro write better code.
I eventually reduced the setup to three main files:
product.mdstructure.mdtech.md
The goal became much simpler: tell Kiro how this codebase works, what conventions and architecture it must follow, and what rules it must not break.
More context is not always better context.
Generate steering, but review it.
You do not have to write steering documents from scratch.
Kiro can generate them from your codebase, and there are ready-made prompts such as the Kiro project initialisation prompt from the AWS Startups prompt library.
Make sure to read any prompt you use, update it to suit your project, and review the generated steering before accepting it.
A generic prompt gives you generic steering.

Use inclusion modes deliberately.
Not every steering file should apply to every task.
Use:
alwaysfor rules that really are global- file-matching inclusion for things such as test conventions or infrastructure rules
- manual inclusion for context that is useful only occasionally
That keeps irrelevant instructions out of everyday requests.
Treat .kiro like software.
It is tempting to keep adding more steering files, hooks, and skills.
But everything in
.kiro becomes a configuration that you have to maintain.Kiro changes. Models change. Your architecture changes. Your own project rules change.
A steering instruction that was useful three months ago can become misleading today.
The leaner the setup, the easier it is to maintain it.
Every correction is a steering candidate.
One habit helped me more than almost anything else.
Whenever I had to correct Kiro, I asked myself:
Would a steering rule have prevented this?
If yes, I updated the steering.
That way each correction improved the next result instead of becoming something I had to explain again in another chat.
Hooks are useful, but they can quietly burn time and credits.
I added hooks because I wanted more automated checks on the generated code.
That part worked.
But hooks can also make Kiro feel unexpectedly slow if they fire too often.
Each hook starts an agent action. If a heavy security or cost review is triggered on every file save, and a feature modifies 30 files, you can easily end up with 30 queued reviews.
That is not a good use of time or credits.
The worst version is when a hook modifies files itself and those modifications trigger the hook again. Kiro has great documentation on the hooks, including the hook types that control when each one is fired.
Security checks were worth automating.
One of the areas where automatic review was especially useful was security.
In a cloud-native project, generated code can look perfectly reasonable while still introducing problems such as:
- overly broad IAM permissions
- overly broad CI/CD permissions
- missing validation
- unsafe handling of external input
- secrets or sensitive values in configuration
- PII data in logs or database where you did not indent it to be
I had security findings in the agentic system that made me realise this kind of review should happen systematically rather than only when I remembered to ask for it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"version": "v1",
"hooks": [
{
"name": "Security Review",
"description": "Review completed agent changes for PII, unsafe logging, missing authorization, missing credit checks, frontend-controlled AI parameters, overly broad IAM, missing idempotency, missing rate limits, committed secrets, and SAM security issues",
"trigger": "Stop",
"action": {
"type": "agent",
"prompt": "Review the changes made in this agent run for security issues. Check for: (1) direct PII fields in application DB models (email, name, phone, address, payment details are forbidden — use Cognito subject ID only), (2) unsafe logging of prompts containing user answers or source material, (3) missing authorization checks on endpoints, (4) missing credit checks before AI calls, (5) frontend-controlled model/token/source/credit/prompt fields — these must be server-controlled, (6) overly broad IAM permissions in SAM templates, (7) missing idempotency keys for credit-spending operations, (8) missing rate limits on AI endpoints, (9) secrets or credentials committed in code, and (10) SAM security issues such as missing encryption, public S3 buckets, or missing log retention. Only report issues relevant to changes made in this agent run. If any issues are found, describe them clearly and identify the affected files or code where possible. If no issues are found, confirm that no security concerns were found."
}
}
]
}Cost should be part of feature design.
The same applies to cost.
In a serverless system, technical design decisions have direct cost consequences:
- The choice of AI model
- Lambda memory and execution time
- S3 requests
- Bedrock tokens
- CloudWatch log volume
- data transfer
- retry behaviour
A cost review is most useful while the feature is being designed, not after the AWS bill arrives.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"version": "v1",
"hooks": [
{
"name": "AI Cost Review",
"description": "Review completed agent changes for Bedrock usage, model choices, token limits, retries, caching, kill switches, and estimated AI cost impact",
"trigger": "Stop",
"action": {
"type": "agent",
"prompt": "Review the changes made in this agent run for AI cost concerns. Check for: (1) new Bedrock calls or AI workflows introduced, (2) model choice — is it the approved model or an unnecessarily expensive alternative, (3) max input/output token settings — are they bounded, (4) retry count — is it within the expected 0-2 range, (5) judge AI usage — is it required and appropriately configured, (6) credit cost — is it defined for this feature, (7) missing daily/monthly usage limits, (8) missing caching for repeatable results, (9) missing kill switch checks at workflow start (AI_GENERATION_ENABLED, AI_CHAT_ENABLED, JUDGE_AI_ENABLED, EXPENSIVE_MODEL_ENABLED), and (10) the estimated cost impact of the changes. Only report concerns relevant to changes made in this agent run. If any cost concerns are found, describe them clearly and identify the affected files or code where possible. If the changes have no AI cost implications, confirm that no AI cost concerns were found."
}
}
]
}What works better for me
I now keep hooks narrow:
- Use specific file patterns.
- Run heavy checks on checkpoints rather than after every task finishes or manually before a PR.
- Keep hooks focused on one responsibility.
- Avoid hooks that run on every save unless they are genuinely lightweight.
I stopped using the strongest model for everything.
My original thinking was simple:
The strongest model should save me the most time.
So I picked the most capable model available and used it for nearly everything.
That turned out to be unnecessary.
You do not need the most expensive model to rename a field, create a straightforward test, or update documentation.
For everyday feature work, Kiro's automatic model selection was good enough and much more economical.
I now normally leave model selection on Auto and only switch manually when I have a specific reason.
There is another subtle advantage: stronger models can sometimes be more argumentative about requirements and try to reinterpret them. For routine implementation work, that is not always what you want.

Keep features small enough that you can still review them.
Kiro is perfectly capable of taking a large spec and generating a very large change.
The problem manifests afterwards – when you have to review it.
A two-thousand-line AI-generated change is not really reviewable.
You either skim it and miss things, or spend a large amount of time checking code you did not write yourself.
I learned to keep features roughly story-sized.
Smaller features give you:
- smaller PRs
- easier code review
- faster feedback
- lower risk when the design changes
- less wasted work if you change direction
That last point matters more with AI-assisted development than I expected.
You often discover something while building that changes how the next part should work.
With a small feature, you (and Kiro) can easily align design and tasks with the new direction.
With a giant spec, you can accidentally end up with old designs and tasks lingering between the new ones.
Use the flow that matches the work.
I did not use the same Kiro workflow for everything.
For new features I typically used spec-driven development:
- Generate & review requirements
- Generate & review the design
- Generate & review tasks
- Execute the tasks
For bugs, I preferred the bug-fix workflow.
That flow starts with reproducing the bug, usually with a test, rather than assuming where the problem is. I sometimes found that this flow discovered that the bug was not present and that the problem was something else, which prevented loads of wasted time and tokens.
For uncertain ideas or experiments, I prefer lighter planning instead of creating a full specification too early.

Run Kiro in parallel when the work is independent.
You do not have to wait for one long-running task before doing anything else.
I often work with multiple Kiro sessions in parallel for independent work.
For example:
- one window builds a feature for a project
- another updates a library which the project consumes
This works especially well when combined with separate branches or Git repositories.
The important part is to keep the work isolated so that two agents are not modifying the same files at the same time.
Connect Kiro to AWS properly.
If Kiro is helping you build and troubleshoot an AWS application, you can connect it to your AWS account through the Agent Toolkit for AWS.
This lets Kiro work with AWS using your configured AWS credentials and the permissions granted to that identity. For example, it can inspect AWS resources and help investigate operational issues such as logs, rather than having to search logs manually and paste them into the chat.
AWS provides a setup prompt from the AWS account home page that configures the Agent Toolkit for your coding agent.

And then I ran out of Kiro credits.
Eventually I ran out of Kiro credits, and it was before project was finished.
That was the point where I finally spent time improving how I used Kiro instead of only focusing on the product I was building.
I still had AWS credit vouchers available, and fortunately Kiro subscriptions can be paid through an AWS account .
That meant I could use AWS credits instead of paying separately.
I did not want to spend extra money, so this option was a great solution to the problem. From now on I only use AWS account to pay for Kiro.
If you are building a startup, it is also worth checking whether you qualify for Kiro for Startups .
What my Kiro setup looks like now
Over time, my approach became much simpler.
I try to give Kiro exactly the context it needs, automate the checks that are worth automating, and avoid making the configuration itself complicated and difficult to keep up to date.
My current rules are:
- Set up
.kiroignoreimmediately. - Keep steering small and relevant.
- Use inclusion modes so context is loaded only when needed.
- Update steering whenever a repeated correction appears.
- Treat
.kiroconfiguration as code that needs maintenance. - Use focused hooks for security and cost.
- Avoid broad hooks that fire constantly.
- Leave model selection on Auto unless there is a reason not to.
- Keep features small enough to review, ideally at story size.
- Use the workflow that matches the task.
- Run independent sessions in parallel.
- Connect Kiro to the AWS account.
- Use AWS credits for Kiro if you already have them available.
The most significant lesson was not that Kiro needed more configuration.
It needed less, but better, configuration.
The more deliberately I controlled context, scope, triggers, and feature size, the more useful Kiro became.
That is how it went from something I was experimenting with into what I wanted it to be from the start: a Lean, Mean Feature-Building Machine.
References
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article