Skip to main content

From Legacy Nightmares to Cloud Success: How We Brought a Monolithic App into the Cloud Era

The Challenges of Working with a Non-Technical Client

When we began this project, we were faced with several challenges, especially since the client had no technical background. Communication gaps were frequent, and the documents provided didn’t offer enough detail to help set up their core legacy app for local development. The client was well-intentioned but lacked the technical understanding to provide the necessary information.

Key Hurdles:

Lack of Maintainability and High Development Costs:

The inconsistent coding practices and fragmented architecture increased the time and effort required for even minor changes, driving up development and maintenance costs significantly.

Poor Knowledge Continuity and Onboarding Challenges:

Frequent team transitions without proper handovers led to repeated learning curves, delays in feature delivery, and dependency on a few individuals familiar with specific parts of the system.

Increased Risk of System Failures:

Tightly coupled, undocumented components and fragile integrations made the system prone to unexpected failures directly impacting business operations and customer trust.

Low Scalability and Inflexibility:

The architecture was not designed with future growth in mind, limiting the ability to introduce new features, integrate with modern systems, or support increased user load without significant rework.

Inconsistent User Experience and Product Behavior:

Redundant or conflicting implementations across modules resulted in an unpredictable user journey and reduced confidence in the platform’s stability and reliability.

Navigating Outdated Documentation and Legacy Technology

Outdated Documentation

Dealing with legacy systems always comes with its challenges. In this case, the documentation we received was outdated. The Entity-Relationship Diagram (ERD) from 2015 made it difficult to understand the current database structure. It was clear that some database tables were no longer in use, but without the proper documentation, we were flying blind.

The Struggle with Local Development

The legacy app relied on a mix of frontend and backend technologies, including Gulp for frontend and uWSGI for backend execution. Setting up the local development environment was a nightmare due to incomplete documentation. No matter how many times we tried different operating systems—Ubuntu, CentOS, and macOS—success remained elusive. After hours of debugging, we finally got their legacy app up and running locally. Victory felt sweet!

From Frustration to Progress: Streamlining the Development Process

Once the legacy app was running locally, we turned our attention to improving the development process for new developers. The solution? Dockerization. By dockerizing the application, we created an image that could easily be replicated, eliminating the need to follow lengthy setup instructions every time.

Key Achievements:

  • Dockerized the application for easy local setup.
  • Improved onboarding for new developers.

Addressing Production Challenges

Unexpected Production Downtime

One of the biggest hurdles was the unexpected production downtime. We were unable to access the production environment because the client hadn’t provided us with the private key. After requesting it, we still faced issues navigating through the different folders and naming conventions. The solution? We dug deep into the Nginx and uWSGI configurations, found the problem (a misconfigured SSL certificate), and fixed it.

Staging vs. Production: A Risky Setup

Another shocking discovery was that both production and staging environments were running on the same server—something we knew was bad practice. We separated the two to reduce the risk of staging outages affecting production.

Key Fixes:

  • Separated staging and production environments.
  • Improved deployment processes by diving deep into boto3 and S3 bucket integrations.

Outdated Architecture: A Legacy Holdover

The Need for an Updated Architecture

The architecture diagram we received was outdated, and the current cloud architecture didn’t match what was on paper. The complexity of multiple EC2 instances on AWS only added to the confusion.

Proposed Improvements:

  • Horizontal scaling to improve performance.
  • Streamlining the connection between different EC2 instances.

Tech Stack Troubles: Legacy and Outdated Systems

The legacy system used an outdated tech stack with AngularJS (which Google abandoned) and old backend packages. We encountered compatibility issues, especially with multiple versions of the same technology.

Key Issues:

  • AngularJS and outdated frontend/ backend tech.
  • Lack of semantic naming conventions in folder structure, makingdebugging a nightmare.

Solution:

  • We planned to gradually update the tech stack and refactor the system for better scalability andsecurity.

Integrating Third-Party Systems: Sunderland Case Study

We received a request from the client to integrate the legacy app with a local system (Sunderland). The two systems had completely different tech stacks and databases—the legacy system used PostgreSQL while Sunderland used MS Access. This integration required creative problem-solving and a careful architectural design, which we detailed in the presentation to Sunderland.

Security and Vulnerability Patching

The Challenge of Legacy Security

Working with legacy systems means constantly managing security vulnerabilities. Our first task was to run an in-house penetration test to identify vulnerabilities before bringing in a third-party tester. We uncovered:

  • 58 vulnerabilities in the backend.
  • 400 vulnerabilities on the frontend

We immediately began patching these issues using Safety Python package and other tools to ensure a more secure application.

Security Issues Found:

  • Injection vulnerabilities.
  • Insecure cookies and authentication.
  • Outdated packages and modules.

We recommended addressing these issues internally before engaging third-party penetration testing.

Scalability and Reliability Concerns

Monolithic Architecture: A Barrier to Scaling

The legacy app followed a monolithic architecture, making it difficult to scale or locate performance bottlenecks. Changing one part of the application often meant rebuilding the entire system, making it a nightmare for both performance and reliability.

Scalability Issues:

  • No horizontal scaling; the whole system had to be scaled as a unit.
  • Hard coupling between different parts of the system, requiring constant rewrites.

Performance Problems

Without horizontal scaling, performance suffered. Database operations were frequent and bulky, leading to high latency and slow load times. The absence of in-memory caching only added to the problem.

Key Performance Challenges:

  • Synchronous database operations.
  • No caching in place, causing significant bottlenecks.

Conclusion: Moving Towards a Better Future

The journey to transition their legacy app to a cloud-based, scalable solution wasn’t easy, but it was a huge learning experience. By tackling outdated documentation, improving the architecture, and addressing security vulnerabilities, we were able to make significant strides toward modernizing the system.

Future Steps:

  • Continue to refactor the tech stack for scalability and security.
  • Implement horizontal scaling and caching to enhance performance.
  • Keep documentation up-to-date to avoid future headaches.

Related Articles

Related Articles