DEV Community

John Vester
John Vester

Posted on

Beyond Code: How to Use AI to Modernize Software Architecture

Article Image

Enterprise teams today ship more code, more frequently, than ever before — fueled in part by AI-driven coding tools like GitHub Copilot and Amazon Q.

But there’s a problem.

While AI helps enterprise teams ship more, faster, the legacy systems they’ve inherited — along with years or even decades of architectural debt — are getting worse. As complexity mounts, many organizations are turning to modernization — not just to keep up, but because cloud migration demands it.

The reality is that while much of the excitement around AI coding focuses on greenfield projects and newly built applications, most developers spend their time maintaining and modernizing legacy systems that are filled with outdated code, old frameworks, and accumulated architectural debt.

Teams have been given this mandate to modernize, but with their AI tools having little to no context of the existing software architecture (or architectural debt), problems such as tight coupling, tangled dependencies, and architectural drift are exacerbated. Developers are quickly discovering AI isn’t improving their architecture … it’s making it worse.

But there’s an answer. Because modernization isn’t just about quickly rewriting code. It’s about reshaping software architecture to support scalability, faster delivery, and cloud-native goals. And with the right approach to using AI, these goals can be achieved.

In this article, we’ll explore how to enable AI-assisted software architecture modernization. While most articles focus on using AI to code, we’ll look at specific examples of how AI can actively help modernize software architecture, including how to create effective prompts. Because with the right tools and the right context, AI can provide consistent, and correct, architectural suggestions that help, not hinder, your team’s efforts..

Whether you’re wrestling with a monolithic legacy system or navigating the intricacies of inherited microservices, AI can be an effective and powerful co-architect.

The Problem with AI and Software Architecture

We know that AI has transformed coding. Most developers rely on AI coding assistants in their daily work, using these AI agents as a smart, experienced, and tireless coding assistant. AI agents can now write (or rewrite) entire blocks of code, and even entire systems if we ask.

The problem is that in this daily use, AI is also affecting architecture.

When a developer writes code, they are constantly asking, and considering the answer to, questions such as:

  • How should I organize my classes?

  • How should I handle caching?

  • Where should I put this business logic?

  • What are the external dependencies?

  • How does this fit into our overall software architecture?

  • And more

Whether developers realize it or not, AI agents are also asking these questions — and implicitly making architecture decisions — with every line of generated code.

And along with these decisions comes risk — especially when the AI agent (as is typical) doesn’t understand any context of the architecture. Your smart, experienced, and tireless coding assistant is making uninformed decisions that on the surface seem correct — but when viewed from the context of software architecture are adding more debt, more problems, and more hours debugging. That “redundant” logic? You know it handles an edge case you found last year. But the AI, when it decides to quietly delete that code, does not.

And to make your problems worse, these mistakes aren’t usually immediately obvious. With legacy systems, code coverage is often less than ideal. And even then, software architecture doesn’t usually announce its breakdown loudly. Problems like tight coupling or unscalable modules are often invisible to monitoring and testing tools. Yet they wreak havoc over time.

Software Architecture Drift

Architecture drift (the slow and nearly invisible erosion away from structured and controlled architecture) is particularly insidious. Consider this Fortune 100 bank that struggled with modernizing a massive Java monolith comprising over 8 million lines of code. It’s a familiar story: the system’s complexity was overwhelming, internal modernization attempts took years, and efforts stalled repeatedly due to hidden architectural pitfalls.

In a recent survey, 93% of respondents reported experiencing negative business outcomes due to misalignment between implemented architecture and documented standards — making the cost of these gaps impossible to ignore.

Architectural problems may be less visible than code bugs, but they are much more problematic.

How AI Can Actually Help Modernize Your Software Architecture

But it doesn’t have to be this way.

AI can not only understand and follow your architecture decisions, but can help improve them as well. In brownfield modernization efforts, where static diagrams and tribal knowledge fall short and developers and architects are left in the dark, AI isn’t just an assistant. It’s a lifeline.

Just as AI tools can detect and recommend improvements at the code level, AI can detect and repair architectural problems during your modernization efforts if it is fed with architectural context based on runtime analysis.

Used properly and with runtime context and preliminary domain driven structural analysis, AI can:

  • Detect and correct architectural antipatterns before they become critical

  • Suggest precise refactoring strategies

  • Prioritize what to fix, conserving an engineering team’s limited time (whereas coding assistants help immediately remediate small-scale issues)

  • Untangle complex dependencies

  • Automate the repair of architectural drift, effectively making the system self-healing

Effective Prompting for Successful Modernization

So we know AI can be our best friend when modernizing systems. But how do we get started?

The first step to effective use of AI in software architecture is effective prompting. The results you get are directly tied to the quality of your prompt. And the quality of your prompt is typically a result of giving the AI context and being specific.

If you are in any way vague in your prompt, the result will most likely be very wrong.

So what does an effective prompt look like?

1. Be specific.

Don’t prompt “Build a REST API to manage orders.”

This prompt, while perhaps exaggeratedly bad, leaves significant room for AI interpretation in both architecture and design.

Instead, prompt specifically with architectural direction:

“Build a Python REST API to manage orders using Flask and SQLAlchemy. Structure the application with:

  • a controller layer (Flask routes) for handling HTTP requests,

  • a service layer for business logic,

  • and a repository layer for data access using the SQLAlchemy ORM.”

2. Be thorough.

Don’t just mention code logic. Also include non-functional requirements.

“Extend the Python Flask REST API to support operational robustness and maintainability. Include the following:

  • Log every incoming HTTP request and its response status. Log execution time for each request using Python’s built-in logging module. Logs should be structured and include a timestamp, endpoint, status code, and latency.

  • Validate all incoming request payloads using Pydantic models at the controller layer. Ensure validation errors return standardized JSON error responses with clear messages and appropriate HTTP status codes.

  • Implement retry logic with exponential backoff for transient database errors in the repository layer. Use Tenacity to handle retries without duplicating logic in the business layer.

  • Ensure that all error responses follow a consistent JSON schema, including error code, message, and optional details for debugging.”

3. Always be an architect.

Convey your architecture design and goals, and how they should be incorporated into the system. Be specific and exact.

“Develop a modular email delivery system for the order system, designed to scale horizontally and can later be separated into its own microservice. The system must follow these architectural goals:

  1. Clear Separation of Concerns — structure the codebase into clean layers:
  • Controller Layer (i.e., the Flask routes): Accepts and validates email send requests.

  • Service Layer: Handles business rules like rate limiting, content templating, and retries.

  • Repository Layer: Manages persistence of email send attempts, statuses, and delivery logs using SQLAlchemy.

2. Statelessness:

  • Do not maintain session or request-specific state between calls.

  • All context (e.g. sender ID, email content, headers) must be carried in each request.

  • Persist all state in external stores (database, cache, or queue) to maintain stateless compute nodes.

3. Scalability and Microservice Readiness:

  • Design each major concern (e.g., send queueing, email rendering, status tracking) as an internal module that could later become an independent service.

  • Avoid hardcoded implementations — interfaces should be defined for each major component (e.g., EmailProvider, EmailQueue, TemplateEngine).

  • Inject all service and repository dependencies via constructors or a simple DI container.

4. Non-functional requirements:

  • Resilience: Gracefully handle SMTP errors and retry failures using backoff strategies.

  • Observability: Log all email activity with request IDs and delivery metadata.

  • Extensibility: Make it easy to support multiple providers (e.g., SendGrid, SES) behind an interface abstraction.”

Remember: prompts effectively are your architecture. Be specific, add context, be thorough, always.

Add architectural context to your prompts

An apt reader will notice that the last statement included “add context” … but we haven’t yet addressed that need.

To add architectural context to our prompts, we need some help. For this example, I’ll use vFunction, a tool that analyzes application architecture and provides structural guidance to GenAI assistants in refactoring. We’ll use static and dynamic analysis combined with data science to understand the logical domains of your app and identify the optimal boundaries of those domains. This in turn allows us to find architectural issues in complex Java and .NET apps, such as:

  • Circular dependencies

  • Anti-patterns

  • Architectural technical debt

  • Domain boundary violations

  • Dead code

  • And more

You then feed that information into your agent to guide its steps, giving your AI agents the information they need to write the code you want. It’s a great example of the kind of information you need to be successful with AI.

A step-by-step example

Let’s walk through the steps above to see how we supply architectural context to code assistants.

Analyze your app.

Start by collecting static and dynamic data from your application. We’ll use vFunction to identify potential logical domains in your application and their boundaries. This baseline allows the tool to identify relevant architectural issues like sub-optimal domain boundaries, inter-domain class dependencies, resource dependencies, high-debt classes, circular dependencies, and shared code across domains. Afterward, you can query the analysis to find errors in need of immediate refactoring, asking questions such as: Which domains contain the highest number of common classes?

Image 3

Analysis of a mid-size app and its entangled business domains (represented by spheres) and accompanying technical debt.

Review the architectural TODOs

Next generate a list of prioritized architectural TODOs, each with a contextual explanation and an AI-ready prompt you can use in tools like Amazon Q or GitHub Copilot.

Image 4

Use the prompt in your AI assistant.

Send the generated prompt into your IDE or code assistant. It will understand the context and help you break up god classes, resolve domain pollution, or implement suggested changes to increase the modularity of your code. For example, in the screen shot above we see suggestions for refactoring a circular flow dependency.

Image 1

Validate and repeat.

Rerun the analysis after applying changes. It will show you whether modularity improved, which TODOs were resolved, which weren’t, and suggest the next step in the refactoring process.

By combining this insight with your LLMs, you create a powerful, context-aware assistant ready to build and to do the hard work of refactoring.

AI is Your Partner in Software Architecture

AI-powered coding is only part of the story. The real value comes from using those same AI assistants not just to generate code but as a partner to help you modernize and improve your existing software architecture.

By using  architecture-aware, runtime based context to generate effective, consistent and precise prompts, AI can help you overcome legacy architecture debt, modularize your code, and implement a scalable, cloud-native, modernized system.

Have a really great day!

Top comments (0)