Building software at scale has been a slogan in enterprises for some time now. With the advent of Internet and applications that are used by a world of users, scalability has became extremely important. Building systems that can support millions of users and peta-bytes of data has become critical to enterprises building software at scale.
Since over a decade now, IaaS and PaaS have become the norm for scaling systems. Cloud computing now offers scaling as a commodity. I can buy scalability with my credit card which is cool. Enterprises are slow at adopting those but still, the problem has a decent solution available.
But the other scalability challenge that most enterprises face and many of the times overlook is in scaling for complexity. Because complexity is more about design rather than number of function points. Design depends on the known patterns at the time of development and as new use cases are added those patterns change while the design remains the same. This is what eventually results in a complex system. Plethora of frameworks are available out there to manage this, but extremely complicated systems can still be built for the simplest of requirements using any one of them.
So how do we scale for complexity then? Based on what I have learned all these years in building, hacking, debugging and managing complex systems here are some of things I think help to build software that scale well for complexity.
Build for the short term, but design for the long term
In this world of agile and scrum, managers in the process of increasing their velocity, sometimes miss out on the larger goals. Product managers in the pursuit of shipping out bunch of features, fail to communicate the larger vision of where the product is going. The waterfall process of design, develop and test have some merits and atleast the design per se I think should have larger span rather than just focusing on the next sprint.
Refactor early and often
Refactoring does not always mean rewriting the entire code. Sometimes it can be as simple as organizing code in a folder heirarchy. It can focus on a small section of your code. These mini refactorings add up and have a compounding effect on decreasing the complexity of the code. If you see that API using the wrong verb, change it, don't live with it.
When the patterns change, design must change
It is not always feasible to build a system anew. Design changes that are abrupt or radical generally don't work out very well. Design change instead should be planned in an iterative manner, slowly. Code decisions should be based on this new, not-yet-in-place design while the transition is being executed. The direction of change sometimes is more important than the change itself.
Date all frameworks, marry with none
All frameworks are originally built as a design for certain pattern and just so it happens that others need software solutions that follow a similar pattern. These frameworks are then generalized a bit and released as open source/commercial software. The keyword here is 'similar'. Lot of times enterprises end up using frameworks to solve design patterns that don't match what those frameworks were originally built for. Open source is good but when you put it on your server, it is no longer open source. It is your code and you will end up living with it. So evaluate it thoroughly. Many times building your own framework using the open source building blocks is a much better strategy than bending a framework to fit your needs.
Mind the estimates
Technical debt is not evenly spread out. A software solution generally has pockets of huge technical debt. The way to identify those pockets is to mind the estimates. When a little feature requires a huge amount of effort, which other wise should have been a trivial task, you know where to look at.
Scaling for complexity and keeping things simple is an ongoing task. May be some day we will have CaaS - 'Complexity as a Service' and our applications will be able to scale for infinite complexity :-) !