Migration & Modernization
From Mainframes to Microservices: Specification-Driven Mainframe Modernization with AI Agents
The Modernization Challenge
Mainframe modernization projects often stall for the same reason; teams cannot capture what the legacy application does with enough precision to rebuild it confidently. The business logic running these applications is sound, it has processed millions of transactions daily for decades. But the applications themselves were written for a different era. They are not structured to support the event-driven, API-first patterns the business needs next. A bank that needs real-time processing across every channel cannot keep waiting for nightly batch reconciliation. The pressure to modernize the application layer grows, but the risk of getting it wrong grows with it.
The real work is not moving code off the mainframe. It starts by reverse-engineering the legacy application into structured, validated business rules, understanding the data structures and flows those rules depend on, and mapping the impact of each component on surrounding applications. Only then can you forward-engineer those rules into modern, cloud-native services with confidence that nothing upstream or downstream breaks silently. This is the Reimagine approach. This is where AWS Transform for mainframe and Kiro address the problem together.
The End-to-End Reimagine workflow
The diagram below (Figure 1) shows how mainframe code becomes cloud-native microservices through a structured sequence of requirements and specifications. It illustrates how AWS Transform for mainframe and Kiro work together through a structured sequence of transformations, each producing a distinct level of specification and requirement that feeds the next.
Figure 1: End-to-end reimagine workflow
The workflow creates three levels of requirements, each serving a different purpose:
| Level | Name | Format | Produced By | Purpose |
| 1 | Legacy Requirements | EARS (REQ-*) | Reverse engineering with AWS Transform for mainframe | Technology-agnostic capture of what the mainframe does |
| 2 | Microservices Specifications | Domain-model specs (Structured Natural Language) | Kiro Domain-Driven Design Analysis Step 1) – sample skill provided by Kiro Power | Implementation-ready service boundaries, APIs, domain models |
| 3 | Modernization Requirements | EARS (REQ-*) | Kiro Specification-Driven Design (Step 2) | Acceptance criteria for code generators and Property-Based Test (PBT) validation |
Easy Approach to Requirements Syntax (EARS) is a structured natural-language template system for writing unambiguous, testable requirements using predefined sentence patterns (such as “When [trigger], the system shall [response]”) to reduce vagueness in specifications.
Reverse engineering with AWS Transform
AWS Transform for mainframe performs deep analysis of the mainframe application in the Assess and Reimagine workflow. You get started with AWS Transform by uploading your mainframe source code into an S3 bucket and creating an Assess and Reimagine job.
Code and data analysis: AWS Transform ingests the source code, classifies artifacts, builds dependency graphs across programs, entry points, and data stores, and maps how programs interact with datastores. This builds the complete inventory that all downstream steps depend on. This includes a data dictionary with field-level definitions and data lineage mapping which programs read and write which datasets.
Business function discovery: From the analysis, AWS Transform identifies the distinct data paths in the application. A data path is the chain of logic executed from an entry point through to a specific output. Data paths sharing the same entry point and output destination are grouped deterministically. An LLM names and describes each group such as Account Management, Transaction Processing, or User Authentication.
Business rule extraction: For the business functions selected, AWS Transform extracts business rules from the source code. Each rule carries a natural-language description, structured acceptance criteria, and exact source-line traceability.
Requirements generation: The final agent in AWS Transform consumes the extracted rules and produces the requirements. These are formal functional specifications that are technology-agnostic and include testable acceptance criteria. AWS Transform generates requirements per business function. Each business function folder includes the following:
- Requirements.md: The full functional requirements, written in structured EARS format with actors, preconditions, user workflows, numbered REQ-* identifiers, validation constraints, and error handling.
- Traceability.yaml: Maps every extracted business rule to a disposition (captured, not applicable, or delegated), ensuring no business logic is lost during the translation from mainframe code to modernization requirements.
These artifacts serve as the foundation that grounds all subsequent AI-driven generation in the forward engineering phase.
To learn more about the Assess and Reimagine workflows in AWS Transform, see Reimagine mainframe applications with traceability and speed using AWS Transform.
Forward engineering with Kiro: From legacy requirement to source code generation
The diagrams below (Figure 2 and Figure 3) shows the forward engineering workflow from legacy requirements to the generated code.
Figure 2: Domain-Driven Design (DDD) to generate microservices specification
Figure 3: Specification-Driven Development workflow using Kiro’s Spec Agent
Step 1: Domain-Driven Design (DDD) to generate microservices specification
The legacy requirements from the reverse engineering flow into Kiro, where Skills files guide a Domain Driven Design decomposition:
- Kiro analyzes and consolidates business functions
- Kiro identifies bounded contexts
- Aggregates which are a cluster of related domain objects, entities, value objects (Immutable object that is defined by its attributes rather than by a unique identity), and domain events are defined within each context
- The bounded contexts are decomposed into microservices
The output is microservice specifications including detailed, implementation-ready service definitions, domain models, API contracts, event schemas, and integration patterns. These specifications are not in EARS format. They are architectural blueprints designed for human review and team alignment.
1a: Prepare the Workspace for specification generation
The forward engineering process begins by establishing a workspace in Kiro that brings together the outputs from AWS Transform requirements generation and the 5-phase specification generation methodology. Structure the workspace as follows:
Organize the AWS Transform outputs
An inputs/ folder contains the structured artifacts from AWS Transform’s requirements generation as shown below:
Below, we show an excerpt from the Account Management business function requirements produced by AWS Transform Reverse engineering output (Level1 Legacy Requirements). These EARS-format requirements capture the precise legacy behavior in a technology-agnostic way, providing the foundation for the DDD decomposition.
5-phase specification generation methodology
The specification generation follows a 5-phase pipeline. Each phase produces a distinct artifact that feeds the next, ensuring consistency, traceability, and quality:
| # | Phase | Purpose | Output |
| 1 | phase-1-business-function-analysis | Reads and synthesizes all inputs/spec/ artifacts into a consolidated analysis preserving full traceability | ddd-analysis.md |
| 2 | phase-2-ddd-bounded-context-analysis | Apply DDD principles to identify bounded contexts, aggregates, entities, value objects, and domain events | DDD bounded context model |
| 3 | phase-3-microservice-spec-generation | Generates one detailed specification per microservice with domain models, API contracts, and REQ-* traceability | <service>-specification.md |
| 4 | phase-4-traceability-verification | Deterministic Python script verifying every REQ-* identifier is traced into specifications | traceability-dashboard.html |
| 5 | phase-5-html-specification-generation | Transforms markdown specs into interactive HTML with styled API cards and Mermaid diagrams | <service>-spec.html |
Methodology packaging: Kiro Power and Skills
The 5-phase methodology is available in two formats. Both are designed as starting points for you to review and adapt to your organization’s architecture standards and modernization goals.
AWS Transform Kiro Power: Packages the methodology, its MCP tools, and best practices into a single installable unit that Kiro activates on-demand. Powers load dynamically based on what you are working on, keeping the context window efficient. Installable with one click from the Kiro marketplace or GitHub repositories (https://github.com/kirodotdev/powers/blob/main/aws-transform/steering/workload-mainframe-reimagine.md).
Skills: Five standalone Skill packages stored under .kiro/skills/ in the workspace. Skills follow the open Agent Skills standard, making them portable across Kiro and other IDEs that support the Skills standard.
Both options deliver the same pipeline and produce similar outputs. The choice depends on tooling preferences and whether you need cross-IDE portability.
1b: Triggering the pipeline
With the workspace prepared, the developer triggers the full specification generation through Kiro’s Spec driven mode with a single prompt, as shown below.
Kiro matches this intent against the methodology instructions and activates each phase in sequence. The methodology guides the generation process with explicit rules for chunked output (handling large codebases without context overflow), cross-referencing between artifacts, and traceability preservation.
Below, we show the output of Step 1 (DDD), a set of Microservice specification requirements (Level 2). Here is an excerpt from the Account Management Service specification showing how the original Legacy Requirements have been transformed into a DDD domain model:
Notice how each value object carries traceability back to the original REQ-* identifiers (e.g., AccountManagement::REQ-F-016, REQ-F-017), maintaining the chain from legacy code to modern domain model.
Step 2: Specification-Driven Design (SDD) to generate code
The microservices specifications feed into Kiro’s SDD workflow. For each microservice, Kiro’s Spec Agent transforms the architectural blueprint into modernization requirements in EARS-format acceptance criteria (WHEN…THE SYSTEM SHALL…) that are precise enough to drive code generation and property-based test derivation. The SDD cycle then proceeds through three structured phases of a Requirements-First Workflow:
-
- requirements.md captures system behavior in EARS-format behaviors
-
- design.md specifies the system architecture with sequence diagrams, data models, and interfaces that satisfy each requirement
-
- tasks.md decomposes the design into discrete, dependency-ordered implementation tasks with clear expected outcomes
Kiro executes these tasks sequentially to produce the generated source code, maintaining full traceability from each requirement through design to implementation. This is explained in detail in the subsequent sections.
2a: Generating requirements.md for code generation
Kiro’s Spec Agent takes the microservice specifications produced in Step 1 (DDD) and analyzes them together with Kiro steering files. Steering files guide Kiro about the target environment and project context for consistency. The key steering files for code generation include:
-
- tech.md: Define the target technology stack (e.g., Spring Boot 3, AWS CDK, PostgreSQL, Angular), frameworks, libraries, and technical constraints so that generated requirements align with the chosen implementation technologies.
-
- product.md: Describes the product’s purpose, target users, key features, and business objectives, so Kiro produces requirements aligned with modernization goals.
-
- structure.md : Outlines the target project’s file organization, naming conventions, module boundaries, and architectural patterns, so that generated requirements map cleanly to the intended codebase layout.
Additional custom steering files can be added to match the enterprise-specific conventions such as authentication flows, error-handling patterns or development framework. The Spec Agent uses this context to produce Level 3 Modernization Requirements with EARS-format acceptance criteria specific to code implementation. They describe what the generated code must do in terms of the target architecture. This includes REST endpoint behaviors, database transaction semantics, event publishing contracts, error handling patterns, and infrastructure provisioning rules. The developer reviews these for technical feasibility before approving. Below, we show an excerpt from the Level 3 Modernization Requirements generated by Kiro’s Spec Agent for the Account Management Service code generation:
2b: Generating design.md for code generation
Once the implementation requirements are approved by the HITL, Kiro generates a design.md document describing the technical implementation strategy. This covers application architecture, database schema design, API implementation, event-driven patterns, and infrastructure architecture.The developer validates that the design adheres to enterprise conventions and confirms the implementation approach before proceeding.
2c: Generating tasks.md for code generation
After design approval, Kiro generates a tasks.md file containing concrete implementation tasks producing actual source code files. Kiro executes these with build-verify-fix loops: after generating each code file, it runs the build, detects compilation errors or test failures, and corrects them autonomously before moving to the next task. This self-healing capability reduces manual intervention during code generation.
Spec-to-Code Validation: Closing the Loop between intent and implementation
Code generation alone is not enough. AWS Transform handles the what with deterministic extraction. DDD handles the how it should be structured (architectural decomposition). Kiro’s Spec Agent handles the how to build and validate the implementation against the specification. The validation workflow described below demonstrates a pattern assembled using Kiro’s custom agent, hook, and steering document primitives. It is not a built-in Kiro feature; rather, it is a recipe that uses these primitives to create a purpose-built validation loop.
Validation Through Specification-Driven Property-Based Test, Integration, and End-to-End tests in Forward Engineering
Property-Based Tests (PBT) validates the generated code against the behavioral contracts. A traceability chain connects every PBT back through modernization requirements → microservice specifications → legacy requirements → original COBOL business rules, ensuring nothing is lost in translation. Kiro transforms EARS acceptance criteria into property-based tests (PBTs) through a direct pipeline. Each acceptance criterion is rewritten as a universal property statement beginning with “for any valid input”. This statement is then converted to executable test code with randomized input generators that produce hundreds of test cases. When a property fails, the framework shrinks the counterexample to a minimal reproducer that traces back to the originating EARS requirement. This identifies whether the defect lies in the implementation or the specification itself.
For mainframe modernization, this matters because COBOL systems accumulate behavioral complexity over decades: packed decimal boundary handling, implicit truncation, cross-convention date logic. These behaviors are captured as EARS acceptance criteria during reverse engineering. PBTs validate that the target microservices preserve those behavioral contracts across thousands of randomized inputs. The structural differences are fundamental (REST boundaries, BigDecimal arithmetic replacing PIC S9(7)V99 COMP-3, distributed persistence replacing monolithic paragraphs). The input generators encode edge cases and overflow conditions beyond what hand-written tests typically cover. Traceability makes this actionable. Every PBT failure traces back to a numbered EARS requirement. Either the target code has a defect, or the specification needs re-examination against the COBOL source. No failure goes unexplained. See below for an example of the Account Balance Finalization Invariant.
Requirement (EARS format):
WHEN finalizing an account, THE Account_Service SHALL atomically update the account balance by adding accrued interest and resetting currentCycleCredit and currentCycleDebit to zero.
PBT Code:
@Tag("Feature: account-management, Property: Account balance update invariant")
class AccountBalanceInvariantProperties {
// new balance == previous balance + accrued interest (scale 2)
@Property(tries = 100)
void newBalanceEqualsOldBalancePlusAccruedInterest(
@ForAll("balances") BigDecimal balance,
@ForAll("interestAmounts") BigDecimal accruedInterest) {
Account account = createAccount(balance, BigDecimal.valueOf(100.00), BigDecimal.valueOf(50.00));
BigDecimal previousBalance = account.getCurrentBalance().amount();
account.finalizeInterestAccrual(accruedInterest);
BigDecimal expectedBalance = previousBalance.add(accruedInterest)
.setScale(2, RoundingMode.HALF_UP);
assertThat(account.getCurrentBalance().amount()).isEqualByComparingTo(expectedBalance);
}
// currentCycleCredit reset to zero
@Property(tries = 100)
void currentCycleCreditIsZeroAfterFinalize(
@ForAll("balances") BigDecimal balance,
@ForAll("interestAmounts") BigDecimal accruedInterest) {
Account account = createAccount(balance, BigDecimal.valueOf(250.00), BigDecimal.valueOf(175.00));
account.finalizeInterestAccrual(accruedInterest);
assertThat(account.getCurrentCycleCredit().amount())
.isEqualByComparingTo(BigDecimal.ZERO.setScale(2));
}
// currentCycleDebit reset to zero
@Property(tries = 100)
void currentCycleDebitIsZeroAfterFinalize(
@ForAll("balances") BigDecimal balance,
@ForAll("interestAmounts") BigDecimal accruedInterest) {
Account account = createAccount(balance, BigDecimal.valueOf(300.00), BigDecimal.valueOf(80.00));
account.finalizeInterestAccrual(accruedInterest);
assertThat(account.getCurrentCycleDebit().amount())
.isEqualByComparingTo(BigDecimal.ZERO.setScale(2));
}
This PBT exercises Account.finalizeInterestAccrual(…) across 100 randomized balance and accruedInterest pairs spanning negative, zero, and positive values at 2-decimal monetary scale, asserting two invariants:
- Balance arithmetic: post-finalize balance always equals previousBalance + accruedInterest (HALF_UP, scale 2). Any drift, subtracting instead of adding, skipping scale normalization, or applying different rounding, fails this property.
- Cycle reset: currentCycleCredit and currentCycleDebit are both zero afterward, regardless of starting values.
Because inputs are generated, the test detects drift across the whole value space including boundary and negative amounts. What it does not cover is the atomicity guarantee this lives in the persistence layer and requires integration testing to validate. The Key ideas for spec-driven tests for validation are as follows:
- Spec-driven test generation: The validator generates tests purely from requirements and design documents. The design document incorporates the output of an API surface extractor that uses Tree-sitter AST analysis to deterministically discover routes, Data Transfer Objects (DTOs), and service interfaces, with no LLM involved in the discovery process. These extracted signatures are embedded in the design document as structural scaffolding, and spec-derived tests are executed against them.
- Three test types from specs alone: Integration tests (per-endpoint: happy paths, validation errors, auth failures, boundary values), end-to-end tests (cross-domain workflows), and property-based tests (randomized bounded inputs for invariants).
- Spec mutation testing: Prevents superficial coverage by mutating one requirement at a time, inverting outcomes, shifting boundaries, substituting status codes, removing side-effects, then regenerating the test and diffing. A killed mutant means real coverage; a survivor means the test is decorative.
- Auto-repair loop: Structured violation reports with varying severity levels, fed back to the coding agent for up to three fix-retest cycles before human escalation. Note, the specs are never modified during repairs, only code changes.
- Assembled from Kiro primitives: Agent definitions, steering documents, three hook types (file-edit, post-task, on-demand), and skills files you drop into a project.
The spec-to-code validation agent, is shown in Figure 4, following a five-step pipeline from specifications to verified code
Figure 4: Spec-to-Code validation agent flow
Conclusion
Mainframe modernization fails when teams cannot capture what the legacy application does with enough precision to rebuild confidently. This approach closes that gap through a layered, traceable pipeline: deterministic extraction with AWS Transform for mainframe, DDD decomposition, Kiro’s spec-driven code generation, and property-based test validation.Traditional modernization demands manual code comprehension, requirements writing, architecture design, implementation, and verification by scarce experts, often spanning years. This workflow compresses the timeline. Requirements are generated with AWS Transform. Design is produced through steering, not discovered using multiple workshops. Implementation is generated from spec constraints, not written from scratch. Validation is automated through derived property-based tests, not manual test authoring.To get started with AWS Transform for mainframe, explore the following resources:
- Getting started with AWS Transform to launch a workspace and create your first Assess and Reimagine job.
- Watch the demo to see the end-to-end workflow in action.
- Learn more about AWS Transform for mainframe for detailed documentation on capabilities and configuration.



