Modernization Challenge of Huge Applications

585-14.jpg

 

Recently I’ve faced a common problem of most of the big applications. With time, due to constant improvements, technologies changes and contribution of different developers, applications become heavy and maze-like. When the application becomes huge, it is hard to control and here many problems arise. The main challenge of app management in such case is that the application just doesn’t work properly. You may ask why this happens if applications are written by ultra smart programmers? The reasons are multiple and different in each case but the common problem is that the logic behind the application is delegated a lot of responsibility.

Single responsibility principle says that each object should not have more than one responsibility.
Of course, for web apps it doesn’t mean that we should handle only simple action within one application. But if we think about this in a more abstract way, we can find that such functionality as, for example, authentication, notifiers, and bunch of other stuff can run by itself independently on each other.

Mobile Development Solutions for Your Next-Gen Apps Unlock the potential of your business with Svitla Systems’ innovative mobile development expertise and tailored solutions. Get a Consultation

Exactly this problem happened with the application of a customer of mine when they applied to Svitla. They have a very huge web application the aim of which in short is to help companies from small to large to sell their products effectively. The logic behind their app has grown so huge that every new change to application became very expensive as it could affect other areas.

The task was challenging. To solve the problem, first of all, my team had to make sure that the logic is fully covered with tests. We have written about 2000 bdd powered tests covering the most important business logic that we wanted to extract.

Our next iteration was dedicated to code extraction. It was kinda fun writing it mostly from scratch. As we used new technologies and frameworks with latest stable versions, we decided to pick Rails-API Gem for smoothly integration with the main application. My customer had already used LDAP (Lightweight Directory Access Protocol) for authentication, so I found Ruby’s net-ldap library very useful and well documented even for a person like me who was not an expert in that sphere.

After a two week iteration, we had a fully working and tested Rails API service that could authenticate users via LDAP.

This solution gave us two things. First, we simplified our code base in core application: it became more manageable with less code and less logic. And, what is more important, we reduced the price of code change.

Second, if something goes wrong on the authentication service, the main app will continue working as it should because now it doesn’t depend on anything else, and logged in users will not feel any discomfort.

Now we continue our extraction process searching for other logic that can work on its own. There’s a lot yet to extract but it’s a great architectural change that is required for everyone who is starting to feel that their apps become fat and hard to manage.

FAQ

What is considered a large application?

A large app is one whose code base and business logic have grown so much and are so interwoven that changes become risky, expensive, and hard to control. Typical signs are many responsibilities piled into a single component and coupling between features (for example, auth and notifications), plus degraded reliability in apps where fixing one area breaks others. In practice, it feels ‘maze-like’, implies development, and raises the cost of change. Applying the single-responsibility principle by extracting independent services (like a separate LDAP-backed auth API) is a common treatment that immediately makes things feel better governed and resilient.

How do you manage state in large applications?

In large apps, keep state by hard separation of worries and cutting down on shared, changeable state. Put important domain state in well-set modules or a single source of truth, and show it through clear APIs while keeping cross-cutting concerns (like login, alerts) in their own services. Make sure rules are kept with full auto tests on business logic to stop problems as the system changes. Pick stateless edges between parts (like HTTP APIs, message queues) so problems or changes in one place don’t spread through the app.

How can you improve the performance of a React application?

Split responsibilities and isolate heavy logic into modules or even into separate services so that the UI has less to manage. Use code splitting and lazy loading to remove as much unnecessary code as possible, and use memoization (React.memo, useMemo, useCallback) to avoid unnecessary re-renders. Create a clean API boundary to fetch only the data that the particular view needs and keep all cross-cutting concerns, from auth, notifications, decoupled from presentation. Back changes with automated tests so that optimizations do not break business logic as the app evolves.

What are large scale applications?

Large-scale applications are those systems in which the codebase and business logic have grown to be extensive, intertwined, and unmanageable to change without side effects. Most of them come with great responsibilities within single components, tight coupling across features, complex dependencies that make the app ‘maze-like.’ Some other symptoms include rising cost of change, frequent regression risk, and operational fragility. The remedy for such a system would be an independently deployable service architecture that enforces single responsibility and protects core logic with comprehensive tests.

How to build a large scale application?

Enforce single responsibility: keep each module focused on one concern, and separate cross-cutting areas (such as authentication, notifications) outside the main path flow of code into independent services with clear APIs to be called. Write comprehensive automated tests around core business logic to lock in behavior and ensure low regression risk as you evolve the system. Use stable, well-supported frameworks and versioned interfaces so that parts can be incrementally extracted and replaced without breaking the whole. Stateless boundaries (HTTP/JSON, message queues) are preferred so failure is contained and resilience is kept in the main app during changes.