When you build things for a living, eventually you'll be faced with trade-offs between quality and delivering on time. Fortunately, with software, cutting corners seldom produces results as devastating as choosing the wrong bolts for a bridge. You can circle back and do it right when time permits.
Yet time is scarce when we're building software faster than ever, propelled by the biggest tech boom since the turn of the century. Who can keep up with enormous demands to crank out legions of Web, mobile, big data, and streaming analytics apps? Quick-and-dirty code and expedient design decisions pile up over time to produce what's known as technical debt. I wouldn't be surprised to learn we're now in the process of accumulating technical debt at historic rates.
[ See what best hardware, software, development tools, and cloud services came out on top in the InfoWorld 2015 Technology of the Year Awards. | Cut to the key news in technology trends and IT breakthroughs with the InfoWorld Daily newsletter, our summary of the top tech happenings. ]
Ultimately, technical debt needs to be paid down -- that is, bad code needs to be refactored, questionable design decisions need to be revisited, and so on -- or you risk the equivalent of a default, when stuff starts breaking all over the place. For a spectacular example, look no further than the Knight Capital Group disaster of 2012, where a software update caused "dead code" to come back to life and lost the trading firm $440 million in 30 minutes.
Microservices architecture potentially offers an easier way to pay down technical debt. Refactoring a big monolithic application can be the equivalent of a balloon payment. But if you break down application functionality into API-accessible microservices, each with a single purpose, you can pay your technical debt incrementally by refactoring services one by one.
Speaking about one of his first microservices projects, legendary agile programming advocate Fred George put it this way:
These little services became almost disposable entities. We didn't really care about keeping them around for long periods of time. If it was useless we threw it away and wrote it again.
Sounds like a nice easy payment plan for technical debt, but it comes with a couple of caveats.
The first is the difficulty of testing microservices-based applications -- a point Martin Fowler and James Lewis made in their classic explanation of microservices. (Their colleague Toby Clemson addressed many of the complexities in a presentation a couple of months ago.) A big chunk of technical debt stems from cutting short test cycles to meet deadlines, and if tests become more complex, the temptation to skip them increases.
Another problem is that it's not always clear what constitutes a microservice. This is new territory for many developers. It's perfectly possible to screw up and do microservices architecture badly -- and bad architecture is the hardest technical debt to pay.
Finally, as Contino CTO Benjamin Wootton observed last year in a post on High Scalability, microservices architecture incurs significant operations overhead:
Where a monolithic application might have been deployed to a small application server cluster, you now have tens of separate services to build, test, deploy and run, potentially in polyglot languages and environments.
All of these services potentially need clustering for failover and resilience, turning your single monolithic system into, say, 20 services consisting of 40 to 60 processes after we've added resilience.
Throw in load balancers and messaging layers for plumbing between the services and the estate starts to become pretty large when compared to that single monolithic application that delivered the equivalent business functionality!
In other words, with microservices, some technical debt is bound to shift from dev to ops, so you'd better have a crack devops team in place.
Despite these complications, microservices architecture seems likely to continue its ascendance, and not only because you can pay down technical debt one service at a time. The original, grassroots developer impulse behind microservices is a powerful motivator: Why not tap a running service instead of coding from scratch? More important, microservices architecture gives businesses the ability to create applications that are more adaptable to new requirements -- or to create entirely new applications based on the same set of services.
But microservices can't work miracles. The most pernicious aspect of technical debt is that few organizations know exactly how much debt they have or where it resides.
When faced with impossible deadlines, the best shops carefully document the corners they're cutting to get the job done. All the trade-offs will be above board and well known, so they can be addressed later. But what about organizations where devs are routinely told they have to do the impossible on time and on budget with no compromises?
In toxic cultures like that, team members have every incentive to keep quiet and let someone else discover the shortcuts at a later date -- why not, since high turnover tends to be the rule in such environments. When management lacks any understanding of what its teams can handle, neither microservices nor any other architecture will help. Debt piles up until default is inevitable.