DEV Community

Cover image for From Legacy Software to Strategic Opportunity: The Starting Point (I)
Dídac Rios
Dídac Rios

Posted on • Edited on • Originally published at didacrios.cat

4 1 2 1

From Legacy Software to Strategic Opportunity: The Starting Point (I)

When we talk about legacy software, we often think of outdated or poorly designed applications. However, the reality is that "legacy" can refer to any application that, while functioning correctly, presents significant challenges for evolution and maintenance. This is the story of how we approached the internalization of a logistics order management system (OMS), with the added challenge of integration with a new e-commerce platform.

The Initial Context

In 2018, the application was developed with the aim of optimizing the order preparation process for a growing e-commerce business and ensuring efficient integration with various logistics operators. Built with PHP (Symfony), MySQL, Socket.io, and jQuery, the application handled everything from packaging to shipping, including shipment tracking, carrier connections, label generation, and performance metrics for order preparation.

For years, this tool fulfilled its purpose. However, over time and with business evolution, significant limitations began to surface.

Accumulated Technical Debt

The technical debt situation was particularly concerning, as it affected multiple layers of the project. On the technological infrastructure level, the application ran on outdated versions of both the framework and the base language:

  • The Symfony version (4.0) was not LTS (Long-Term Support) and had stopped receiving security updates as of January 2019.
  • PHP 7.1 had also reached its end-of-support cycle, leaving the system without critical security updates.

Beyond outdated versions, the project showed significant deficiencies in fundamental aspects of software development:

  • Non-existent or inadequate testing: The lack of automated tests (unit, integration, and end-to-end) not only made early error detection difficult but also turned any modification into a potential risk for system stability.

  • Lack of code standards: The codebase did not follow documented patterns or standards, and those applied were not aligned with industry best practices. This complicated both maintenance and the on-boarding of new developers to the project.

  • Insufficient documentation: Existing documentation was scarce and often incomplete. This impacted not only technical development but also the understanding of business processes implemented in the code.

  • Poor version control: The Git history was poorly descriptive, with non-granular commits and messages that followed no conventions or provided context about the changes made. This made it hard to understand the code's evolution and the decisions made over time.

This accumulation of technical debt not only posed a risk to the system's stability and security but also:

  • Slowed the development pace for new features.
  • Increased the risk of introducing errors.
  • Hindered the on-boarding of new team members.
  • Increased maintenance costs.
  • Complicated problem diagnosis and resolution.

Structural Limitations

The initial architecture presented coupling issues that severely affected its flexibility and scalability:

  • Total dependency on the main e-commerce platform: The application could not operate autonomously, as all logistics operations depended directly on the data and processes of the e-commerce platform. This meant that any change to the main platform could break the system's functionality.
  • Shared database causing performance issues: Both the logistics application and the e-commerce platform used the same database, leading to performance problems, especially during peak loads for either application. Additionally, this setup complicated permission management, as any database access could compromise critical data from other systems.
  • Inability to operate independently: The application was designed to work exclusively with the e-commerce platform. This not only limited its portability but also complicated testing in isolated environments or migration to other platforms. Its dependencies were not properly encapsulated, making any attempt to isolate it require massive and costly system-wide changes, with main classes failing to adhere to the Single Responsibility Principle.
  • Difficulty implementing new features: The lack of adherence to principles like Open/Closed (OCP) and Liskov Substitution (LSP) made system evolution extremely challenging. New features required modifying existing code, increasing the risk of introducing regressions. Moreover, direct module dependencies made it nearly impossible to follow the Dependency Inversion Principle (DIP).

These structural limitations not only reduced the system's maintainability and scalability but also increased the risks associated with any modification or evolution, placing the application in a technically fragile and strategically vulnerable state.

Development Management and Strategic Alignment

One of the most significant challenges was not only technical but also strategic. External development, while functionally correct, presented important limitations in the organizational domain:

  • Disconnection from overall strategy: Development was carried out in isolation, without a complete view of the company internal goals and processes. As a result, functionalities that were technically correct did not always align with the business actual needs.
  • Lack of strategic prioritization: New features were implemented without a clear vision, valuation and prioritization process. There was no questioning of whether a feature was truly necessary, the best way to implement it, or whether there were more efficient alternatives.
  • Reactive vs. proactive development: Development followed a largely reactive pattern, addressing immediate needs without considering the long-term impact or potential synergies with other company processes.
  • Absence of validation processes: The lack of a structured review and validation process resulted in functionalities that, while operational, were not always the best solution for end users or the company's overall objectives.

This situation was unsustainable in the long term, as it:

  • Produced a product increasingly misaligned with real needs.
  • Complicated integration with other systems and company processes.
  • Hindered strategic decision-making about the product.
  • Limited the team's capacity for innovation and continuous improvement.

The Basal Cost Impact

An often-overlooked but particularly relevant aspect of this project was the basal cost, a key concept in software development that refers to the minimum cost required to keep a system operational, even without adding new features or improvements.

In our case, the basal cost included all expenses related to maintaining outdated versions of the framework and language, addressing urgent issues stemming from accumulated technical debt, managing dependencies with other systems, adapting to a tightly coupled architecture, and lacking domain knowledge. These factors consumed a significant portion of available resources, directly impacting the team's ability to invest in innovation and continuous improvement.

While the basal cost was not the sole reason behind the decision to internalize development, it played a crucial role in the initial diagnosis of the project. Ignoring basal costs when evaluating the sustainability of a system can lead to overlooked risks, but in this case, it was clear evidence that the current strategy was unsustainable in the long term. As we will explore in subsequent articles, any attempt to maintain the existing structure would have led to exponential increases in the basal cost over time.

For a more detailed explanation of the basal cost concept and its implications, I recommend consulting Eduardo Ferro's original article.


The Turning Point: A New Challenge and Strategic Decision

In any refactoring project, multiple strategies can be adopted, and it is common to face the dichotomy of: the strangler fig strategy or starting anew with a "big bang rewrite."

Red pill blue pill meme, the question says Choose Refactor Strategy and options between Strangler Fig and Big Bang Rewrite

Initially, the technical decision was to work within the same legacy project, applying the Strangler Fig strategy, an approach that involves developing a new module or system that progressively replaces parts of the old system. This strategy allowed us to make parallel changes, reducing risks and maintaining current functionality while building a more solid foundation for future features.

However, from a business perspective, this option was considered too risky for the current system, which was already operational and meeting its functions. The decision was made to avoid modifying the existing project and instead focus on developing an independent application that met the new requirements.

This change of direction led us to fork the existing codebase, a decision that, while technically feasible, came with certain challenges:

  • Codebase duplication: Two separate codebases now needed to be maintained.
  • Separate databases: Data structures had to be duplicated and adapted for each system.
  • Replicated infrastructure: Independent servers had to be deployed, ensuring adequate observability for each system.
  • Increased cognitive load for the team: All these duplication required additional effort to maintain consistency between the two systems, increasing the team's complexity and error risk.

This approach allowed us to move towards an independent solution, ensuring the existing system's stability while developing a project aligned with the new strategic objectives. However, it was crucial to analyze the pros and cons in detail to tackle this challenge with confidence and planning. Additionally, a business-level commitment was made to avoid expanding functionalities and maintain strict control over the project backlog until the migration to the new e-commerce system was complete.

New architecture diagram

Pros Cons
Working in a non-productive environment, reducing production risks Temporarily maintaining multiple projects
Freedom to implement new technologies and patterns from scratch Temporary duplication of maintenance efforts
No concern for technical limitations or dependencies of the old system Duplicating functionalities may slow development due to synchronization needs
Ability to focus exclusively on necessary functionalities Risk to deadlines from managing two codebases
Opportunity to implement best practices from the beginning Complexity in project management
Greater ease of incorporating testing from the start Need to maintain compatibility with historical data
Flexibility to adapt to new business requirements Higher initial cost in time and resources
Better alignment with the company's overall strategy Possible temporary loss of non-essential functionalities

Conclusions and Next Steps

The decision to internalize and rewrite legacy software is never easy, especially when the software fulfills its function. The saying "if it ain't broke, don't fix it" will always be present. However, sometimes it is necessary to take one step back in order to take two steps forward.

The next steps in this process involve a gradual migration strategy that focuses on the following:

  1. Prioritize features based on their business impact and technical cost.
  2. Adopt automated testing and CI/CD practices to ensure the stability of new developments.
  3. Improve team training and communication to ensure alignment between business and development.

The ultimate goal is to deliver a product that not only meets the company's operational needs but also ensures its future growth. As with any process of change, this will not happen overnight, but the team is committed to moving forward one step at a time.

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

👋 Kindness is contagious

Explore a sea of insights with this enlightening post, highly esteemed within the nurturing DEV Community. Coders of all stripes are invited to participate and contribute to our shared knowledge.

Expressing gratitude with a simple "thank you" can make a big impact. Leave your thanks in the comments!

On DEV, exchanging ideas smooths our way and strengthens our community bonds. Found this useful? A quick note of thanks to the author can mean a lot.

Okay