DEV Community

Olaoluwa Marvellous
Olaoluwa Marvellous

Posted on

Building CodeCrypt: How Kiro's Spec-Driven Development Changed My Approach to Complex Software

The Challenge: Resurrecting Dead Code
For the Kiroween Hackathon, I set out to build CodeCrypt—a VS Code extension that resurrects abandoned GitHub repositories by modernizing dependencies, fixing vulnerabilities, and proving functional equivalence through "Time Machine" validation.

The scope was ambitious: AST analysis, LLM integration, Docker orchestration, real-time 3D visualizations with Three.js, and a live audio "Resurrection Symphony." This wasn't a weekend CRUD app.

I needed a development approach that could handle this complexity without collapsing into chaos.

Enter Kiro's Spec-Driven Development
Kiro introduced me to a workflow I'd never experienced before: spec-driven development with property-based testing baked in from day one.

Instead of jumping straight into code, Kiro guided me through three phases:

  1. Requirements (EARS + INCOSE) Kiro helped me write requirements using the EARS (Easy Approach to Requirements Syntax) pattern:

WHEN a user provides a GitHub URL
THEN the system SHALL clone the repository and analyze dependencies

WHEN dead URLs are detected in package.json
THEN the system SHALL identify replacement packages from the registry
Every requirement followed strict quality rules:

Active voice (who does what)
No vague terms ("quickly", "adequate")
One thought per requirement
Measurable conditions
This forced clarity I'd never achieved with typical user stories.

  1. Design with Correctness Properties Here's where it got interesting. Before writing any code, Kiro helped me define correctness properties—universal rules that should hold across all inputs.

For example, for the lockfile parser:

Property 1: Round-trip consistency For any valid lockfile, parsing then serializing should produce an equivalent structure.

Property 2: Dependency graph completeness For any package in the lockfile, all transitive dependencies should be resolvable.

These weren't just documentation. They became executable tests.

  1. Implementation Plan with Property-Based Tests Kiro generated a task list where each correctness property became a property-based test task:
  • [ ] 2.1 Implement lockfile parser
  • [ ] 2.2 Write property test for round-trip consistency
    • Property 1: Round-trip consistency
    • Validates: Requirements 3.1
  • [ ] 2.3 Write property test for dependency resolution
    • Property 2: Dependency graph completeness
    • Validates: Requirements 3.2 Each property test was explicitly linked back to requirements and design.

The Property-Based Testing Revelation
I'd heard of property-based testing but never used it seriously. Kiro made it the default.

Instead of writing:

test('parses npm lockfile', () => {
const result = parseLockfile(sampleLockfile);
expect(result.dependencies).toHaveLength(5);
});
I wrote:

/**

  • Feature: lockfile-parser, Property 1: Round-trip consistency
  • Validates: Requirements 3.1 */ test('lockfile round-trip preserves structure', () => { fc.assert( fc.property(lockfileGenerator(), (lockfile) => { const parsed = parseLockfile(lockfile); const serialized = serializeLockfile(parsed); const reparsed = parseLockfile(serialized); expect(reparsed).toEqual(parsed); }), { numRuns: 100 } ); }); This single test ran 100 times with randomly generated lockfiles, catching edge cases I never would have thought to test manually.

Real Bugs Caught by Property Tests
Property-based testing found bugs that would have shipped:

Bug 1: URL Validation Edge Case My URL validator worked fine for http:// and https:// but failed on git:// protocols. The property test generated random URL schemes and caught it immediately.

Bug 2: Transitive Dependency Cycles My dependency resolver worked for simple cases but infinite-looped on circular dependencies. The property test generated random dependency graphs and exposed the issue.

Bug 3: Whitespace Handling in Parsers My lockfile parser failed on files with mixed tabs/spaces. Property tests with random whitespace combinations caught it.

The Numbers
By the end of the hackathon, CodeCrypt had:

100+ property-based tests covering core logic
50+ unit tests for specific examples
15+ integration tests for end-to-end flows
Zero known bugs in the core resurrection pipeline
More importantly, I had confidence. Not hope—actual mathematical confidence that my code worked across the input space.

What Changed in My Approach
Before Kiro:
Write code
Write some unit tests
Hope it works in production
Debug when it doesn't
After Kiro:
Define what "correct" means (requirements)
Formalize correctness properties (design)
Write property tests (implementation)
Write code until tests pass
Ship with confidence
The Spec-Driven Advantage
The spec documents became living artifacts:

requirements.md → What the system should do design.md → How it should work (with correctness properties) tasks.md → Step-by-step implementation plan

When I needed to add features or fix bugs, I updated the spec first. The implementation followed naturally.

This was especially powerful for the complex parts:

Hybrid AST + LLM analysis
Docker-based time machine validation
Real-time event streaming to the frontend
Each had clear requirements, explicit correctness properties, and comprehensive property tests.

Lessons Learned

  1. Correctness is a design decision, not a testing afterthought

Defining what "correct" means before writing code changes everything. Property-based testing makes those definitions executable.

  1. Property tests complement unit tests

Unit tests verify specific examples. Property tests verify universal rules. You need both.

  1. Specs reduce cognitive load

With clear requirements and design docs, I never wondered "what should I build next?" The task list told me exactly what to do.

  1. AI pair programming works best with structure

Kiro's spec-driven workflow gave the AI clear context. Instead of vague prompts like "make it better," I could say "implement Property 3 from the design doc."

Try It Yourself
If you're building something complex, try this workflow:

Write requirements using EARS patterns
Define correctness properties before coding
Turn properties into property-based tests
Implement until tests pass
You'll catch more bugs, ship with more confidence, and actually understand what your code is supposed to do.

The Result
CodeCrypt shipped with:

A working resurrection pipeline
3D Ghost Tour visualisation
Live metrics dashboard
AI narrator
Resurrection Symphony
Time Machine validation
All built in a hackathon timeframe, with comprehensive test coverage and zero known bugs in core functionality.

That's the power of spec-driven development with property-based testing.

What's your approach to testing complex systems? Have you tried property-based testing? Drop a comment below!

Built with Kiro during the Kiroween Hackathon. Check out CodeCrypt on GitHub: https://github.com/ola-893/codecrypt-kiro

Top comments (0)