Goal: A new developer joins your team and is productive within 1 day.
This is your "Day 1" document. Follow it top-to-bottom.
Most tooling is installed automatically via ThothCTL:
thothctl init envThis bootstraps your local environment with:
- Node.js 24+ (via nvm)
- AWS CDK CLI (latest v2)
- Docker Desktop (required for local development)
- AWS CLI v2 (configured with SSO or credentials)
- ThothCTL (the framework CLI)
- pnpm or npm (package manager)
- Git (with commit hooks pre-configured)
| Tool | Verify | Minimum Version |
|---|---|---|
| Node.js | node --version |
20.x |
| Docker | docker --version |
24.x |
| AWS CLI | aws --version |
2.x |
| CDK | cdk --version |
2.170+ |
| ThothCTL | thothctl --version |
latest |
Ensure you have:
- AWS SSO configured (
aws sso login --profile <your-profile>) - Access to the team's dev account
- Permissions to deploy CloudFormation stacks
# Clone the project scaffold
git clone <your-team-repo-url>
cd <project-name>
# Install dependencies
npm install
# Verify everything compiles
npm run buildproject-root/
├── .kiro/ # AI steering files (specs, tasks, rules)
├── bin/
│ └── app.ts # CDK app entry point
├── lib/
│ ├── stacks/ # CDK stack definitions
│ └── constructs/ # Reusable L3 constructs
├── src/
│ ├── domain/ # Pure business logic (no AWS imports!)
│ │ ├── models/ # Domain entities and value objects
│ │ ├── services/ # Domain services / use cases
│ │ └── ports/ # Interfaces (inbound + outbound)
│ ├── adapters/
│ │ ├── inbound/ # Lambda handlers, API controllers
│ │ └── outbound/ # DynamoDB repos, SQS publishers, HTTP clients
│ └── shared/ # Cross-cutting: logging, errors, config
├── test/
│ ├── unit/ # Fast, isolated tests
│ ├── integration/ # Tests with real AWS (localstack or deployed)
│ └── cdk/ # CDK snapshot + cdk-nag tests
├── docker-compose.yml # Local development environment
├── cdk.json # CDK configuration
├── thoth.config.ts # ThothCTL configuration
└── package.json
Key insight: The src/domain/ folder should NEVER import from @aws-sdk/*. If it does, the architecture boundary has been violated.
This framework uses the Hexagonal Architecture (Ports & Adapters) pattern:
┌─────────────────────────────┐
Inbound │ DOMAIN │ Outbound
Adapters │ │ Adapters
│ ┌───────────────────────┐ │
┌──────────┐ │ │ Business Logic │ │ ┌──────────────┐
│ Lambda │────▶│ │ (pure functions) │ │────▶│ DynamoDB │
│ Handler │ │ │ │ │ │ Repository │
└──────────┘ │ └───────────────────────┘ │ └──────────────┘
│ ▲ │ │
┌──────────┐ │ │ ▼ │ ┌──────────────┐
│ SQS │────▶│ ┌──────┴───┐ ┌──────────┐ │────▶│ EventBridge │
│ Consumer │ │ │ Inbound │ │ Outbound │ │ │ Publisher │
└──────────┘ │ │ Ports │ │ Ports │ │ └──────────────┘
│ └──────────┘ └──────────┘ │
└─────────────────────────────┘
| I need to... | Put it in... |
|---|---|
| Define a business rule | src/domain/services/ |
| Define a data entity | src/domain/models/ |
| Define an interface for external systems | src/domain/ports/ |
| Handle an incoming Lambda event | src/adapters/inbound/ |
| Read/write to DynamoDB | src/adapters/outbound/ |
| Publish events to EventBridge | src/adapters/outbound/ |
| Call an external HTTP API | src/adapters/outbound/ |
| Define CDK infrastructure | lib/stacks/ or lib/constructs/ |
Domain logic is framework-agnostic. It doesn't know about Lambda, DynamoDB, or SQS. Adapters translate between the outside world and your domain.
The development loop follows a progressive confidence path:
Local (fast) ──▶ Express Deploy (minutes) ──▶ Pipeline (production)
# Start local services (DynamoDB Local, LocalStack, etc.)
docker compose up -d
# Run your function locally with hot reload
npm run dev
# Or invoke a specific function
npm run invoke -- --function MyFunction --event events/sample.json# Deploy directly to your personal dev account (skips pipeline)
cdk deploy --all --profile dev --hotswap-fallback
# For even faster iteration on Lambda code only:
cdk deploy --all --profile dev --hotswap
--hotswapupdates Lambda code in ~5 seconds without a full CloudFormation deployment.
git add .
git commit -m "feat: add order validation logic"
git push origin feature/order-validation
# Open a PR → pipeline runs → deploys to staging → approval → production| Stage | Speed | Confidence |
|---|---|---|
| Unit tests locally | Seconds | Code correctness |
| Docker Compose | Seconds | Integration logic |
| Express deploy | 1–3 min | Real AWS services |
| Pipeline (staging) | 5–10 min | Production parity |
| Pipeline (prod) | +approval | Full confidence |
Every feature or task you work on follows the AI-DLC pattern. Start your prompt to the AI assistant with:
Using AI-DLC, implement <description of what you need>
This triggers a structured lifecycle:
The AI will:
- Clarify requirements and ask questions
- Generate a specification document
- Define acceptance criteria
- Identify affected components and dependencies
- Produce a task breakdown
Your role: Answer questions, validate the spec, approve the design.
The AI will:
- Implement code following the hexagonal architecture
- Write unit tests alongside implementation
- Run
cdk-nagcompliance checks - Follow framework conventions automatically
Your role: Review the implementation, run tests, provide feedback.
The AI will:
- Add observability (structured logging, metrics, alarms)
- Generate runbook entries
- Update documentation
- Verify pipeline compatibility
Your role: Approve the PR, monitor the deployment.
Using AI-DLC, add a new endpoint POST /orders that validates the order,
stores it in DynamoDB, and publishes an OrderCreated event to EventBridge.
The AI handles the boilerplate. You focus on business decisions.
The .kiro/ directory contains files that guide AI behavior for your project:
.kiro/
├── specs/ # Feature specifications (AI-DLC artifacts)
│ ├── order-service.md # Spec for the order service feature
│ └── auth-flow.md # Spec for authentication flow
├── tasks/ # Task breakdowns for in-progress work
│ └── current-sprint.md
├── rules/ # Project-specific rules the AI must follow
│ ├── architecture.md # "Always use hexagonal pattern"
│ ├── naming.md # "Lambda handlers use handle* prefix"
│ ├── testing.md # "Every adapter needs integration test"
│ └── security.md # "Never log PII fields"
└── steering.md # Top-level project context
When you work with an AI assistant (Kiro, Copilot, etc.), these files provide:
- Context: The AI understands your project's architecture and conventions
- Constraints: Rules prevent the AI from generating non-compliant code
- Continuity: Specs persist across sessions — pick up where you left off
- Consistency: Every team member gets the same AI guidance
- Read
steering.md— it's the project overview - Check
rules/— understand what conventions are enforced - Look at existing
specs/— see how features are documented - Don't edit rules without team consensus — they affect everyone
ThothCTL is your framework CLI. These are the commands you'll reach for every day:
Analyze your project for compliance and best practices:
# Full scan — architecture, security, performance
thothctl scan
# Scan specific category
thothctl scan --category security
thothctl scan --category architectureCatches issues like:
- Domain importing from AWS SDK (architecture violation)
- Missing error handling in Lambda handlers
- Unencrypted resources in CDK stacks
- Missing alarms for critical functions
Quick validation before committing:
# Pre-commit check (runs automatically via git hook)
thothctl check
# Check specific aspect
thothctl check --type types # TypeScript strict mode compliance
thothctl check --type deps # Dependency vulnerabilities
thothctl check --type cdk-nag # CDK security/complianceManage development workflows:
# Start a new feature workflow
thothctl workflow start --feature "order-validation"
# See current workflow status
thothctl workflow status
# Complete current workflow step
thothctl workflow nextAuto-generate documentation from your code:
# Generate API docs from CDK constructs
thothctl document --type api
# Generate architecture decision records
thothctl document --type adr
# Update README with current project state
thothctl document --type readme| Command | When to use |
|---|---|
thothctl scan |
Before opening a PR |
thothctl check |
Before every commit (auto via hook) |
thothctl workflow start |
Beginning a new feature |
thothctl document |
After significant changes |
thothctl init env |
First-time setup |
We follow trunk-based development with short-lived feature branches.
feature/<ticket-id>-short-description
fix/<ticket-id>-short-description
chore/<description>
# Start from main (always up to date)
git checkout main
git pull
# Create a short-lived branch
git checkout -b feature/ORD-123-add-order-validation
# Work, commit often with conventional commits
git commit -m "feat(orders): add validation for order amounts"
git commit -m "test(orders): add unit tests for validation"
# Push and open a PR
git push -u origin feature/ORD-123-add-order-validation
gh pr create --title "feat(orders): add order validation" --body "..."<type>(<scope>): <description>
Types: feat, fix, refactor, test, chore, docs
Scope: the domain area (orders, auth, infra, etc.)
- Branch lifetime: < 1 day ideal, < 3 days maximum
- Size: < 400 lines changed (split larger work)
- Reviews: 1 approval minimum
- Checks must pass: tests, cdk-nag, thothctl scan
- Squash merge into main
Push to main → Pipeline → Build → Test → Deploy Staging → Approval → Deploy Prod
test/
├── unit/ # Mirror of src/ structure
│ ├── domain/
│ │ └── services/
│ │ └── order-service.test.ts
│ └── adapters/
│ └── inbound/
│ └── create-order-handler.test.ts
├── integration/ # Tests against real/emulated services
│ └── order-repository.integration.test.ts
└── cdk/ # Infrastructure tests
├── snapshot.test.ts # Detect unintended CDK changes
└── compliance.test.ts # cdk-nag rule validation
# Run everything (unit + cdk-nag) — this is what CI runs
npm test
# Run only unit tests
npm run test:unit
# Run with coverage
npm run test:coverage
# Run a specific test file
npx jest test/unit/domain/services/order-service.test.ts
# Run integration tests (requires Docker or AWS credentials)
npm run test:integration
# Run CDK compliance tests only
npm run test:cdk- Unit tests — Jest runs all
*.test.tsfiles intest/unit/ - CDK-nag tests — Validates your infrastructure against AWS best practices (security, reliability, compliance rules)
Both must pass for CI to go green.
| Layer | What to test | How |
|---|---|---|
| Domain services | Business logic, edge cases | Pure unit tests (no mocks needed!) |
| Inbound adapters | Event parsing, error handling | Unit tests with mocked ports |
| Outbound adapters | Correct AWS SDK calls | Integration tests |
| CDK stacks | Security compliance, no regressions | cdk-nag + snapshots |
// test/unit/domain/services/order-service.test.ts
import { validateOrder } from '@domain/services/order-service';
describe('OrderService', () => {
describe('validateOrder', () => {
it('rejects orders with zero amount', () => {
const order = { id: '123', amount: 0, currency: 'USD' };
const result = validateOrder(order);
expect(result.isValid).toBe(false);
expect(result.errors).toContain('Amount must be greater than zero');
});
it('accepts valid orders', () => {
const order = { id: '123', amount: 99.99, currency: 'USD' };
const result = validateOrder(order);
expect(result.isValid).toBe(true);
});
});
});-
Create the domain logic (if new):
// src/domain/services/my-feature-service.ts export function processFeature(input: FeatureInput): FeatureOutput { // Pure business logic here }
-
Create the inbound adapter (Lambda handler):
// src/adapters/inbound/my-feature-handler.ts import { processFeature } from '@domain/services/my-feature-service'; import { MyRepository } from '@domain/ports/my-repository.port'; export const handler = async (event: APIGatewayProxyEventV2) => { const input = parseEvent(event); const result = processFeature(input); return { statusCode: 200, body: JSON.stringify(result) }; };
-
Add CDK infrastructure:
// lib/constructs/my-feature-function.ts const fn = new NodejsFunction(this, 'MyFeatureFunction', { entry: 'src/adapters/inbound/my-feature-handler.ts', handler: 'handler', runtime: Runtime.NODEJS_24_X, environment: { TABLE_NAME: table.tableName }, }); table.grantReadWriteData(fn);
-
Add tests in
test/unit/domain/services/andtest/unit/adapters/inbound/ -
Run
thothctl scanto validate compliance
// In your API construct or stack
const api = new HttpApi(this, 'Api');
api.addRoutes({
path: '/orders/{id}',
methods: [HttpMethod.GET],
integration: new HttpLambdaIntegration('GetOrder', getOrderFn),
});Then create the handler following the pattern above.
-
Create the handler:
// src/adapters/inbound/order-created-consumer.ts import { SQSEvent } from 'aws-lambda'; import { processOrderCreated } from '@domain/services/notification-service'; export const handler = async (event: SQSEvent) => { for (const record of event.Records) { const orderEvent = JSON.parse(record.body); await processOrderCreated(orderEvent); } };
-
Wire up the infrastructure:
const queue = new Queue(this, 'OrderCreatedQueue', { deadLetterQueue: { queue: dlq, maxReceiveCount: 3 }, }); const consumer = new NodejsFunction(this, 'OrderCreatedConsumer', { entry: 'src/adapters/inbound/order-created-consumer.ts', }); consumer.addEventSource(new SqsEventSource(queue, { batchSize: 10 }));
-
Connect to EventBridge (if sourcing from EventBridge):
new Rule(this, 'OrderCreatedRule', { eventBus, eventPattern: { source: ['orders'], detailType: ['OrderCreated'] }, targets: [new SqsQueue(queue)], });
Cause: TypeScript path aliases not configured.
Fix: Check tsconfig.json has path mappings:
{
"compilerOptions": {
"paths": {
"@domain/*": ["./src/domain/*"],
"@adapters/*": ["./src/adapters/*"]
}
}
}And ensure Jest config has moduleNameMapper set accordingly.
Cause: Using broad managed policies like AdministratorAccess.
Fix: Replace with scoped inline policies or use grant* methods:
// ❌ Don't do this
role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName('AmazonDynamoDBFullAccess'));
// ✅ Do this instead
table.grantReadWriteData(fn);Cause: Docker Desktop not running.
Fix: Start Docker Desktop. On WSL2, ensure Docker integration is enabled for your distro.
Cause: Resource name collision (often from hardcoded physical names).
Fix: Remove hardcoded tableName, functionName, etc. Let CDK generate unique names:
// ❌ Fragile
new Table(this, 'Orders', { tableName: 'orders-table' });
// ✅ Resilient
new Table(this, 'Orders', { /* tableName omitted — CDK generates it */ });Cause: Domain code imports AWS SDK or adapter code.
Fix: Move the AWS-specific code to an adapter and inject it via a port:
// ❌ In domain/services/
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; // VIOLATION
// ✅ In domain/ports/
export interface OrderRepository {
save(order: Order): Promise<void>;
}
// ✅ In adapters/outbound/
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
export class DynamoOrderRepository implements OrderRepository { ... }Cause: Usually timezone, environment variable, or dependency caching issues.
Fix:
- Ensure tests don't depend on local time — use UTC
- Check that all env vars are set in CI config
- Run
npm ci(notnpm install) in CI for deterministic installs
Fix:
aws sso login --profile <your-profile>
export AWS_PROFILE=<your-profile>| Doc | What you'll learn |
|---|---|
docs/01-philosophy.md |
Why this framework exists |
docs/02-architecture.md |
Deep dive on hexagonal pattern |
docs/05-ai-dlc.md |
Full AI-DLC methodology |
docs/07-thothctl.md |
Complete ThothCTL reference |
docs/10-testing-strategy.md |
Testing pyramid and patterns |
docs/15-observability.md |
Logging, metrics, alarms |
docs/20-pipeline.md |
CI/CD pipeline details |
Copy this and check off items as you go:
[ ] Run `thothctl init env` and verify all tools installed
[ ] Clone the repo and run `npm install`
[ ] Run `npm test` — all tests pass
[ ] Read `.kiro/steering.md` for project context
[ ] Read `.kiro/rules/` for coding conventions
[ ] Start Docker and run `docker compose up -d`
[ ] Run `npm run dev` to see local execution
[ ] Do an express deploy: `cdk deploy --all --profile dev --hotswap`
[ ] Try "Using AI-DLC, add a health check endpoint" as practice
[ ] Open your first PR using the git workflow above
You're ready. Welcome to the team. 🚀
| Previous | Next |
|---|---|
| ← Adoption Roadmap | Custom Intelligent AI Brains → |