Nanoservices - Embracing change.
Software teams often find that successfully and responsively changing their systems to address new customer requests seems to depend more on luck than on judgement. Did they ask for something that fits with the model of the problem you’ve already encoded in the software? Lucky you! You’ll quickly get this new feature in, with few problems. Does the new request challenge fundamental assumptions about the data flow, or separation of responsibilities, in the system? You’re down on your luck, and have a long, complex journey ahead to get this feature working.
Some teams try to game this problem through their estimates, effectively punting the difficult-sounding stories down the road by claiming they’ll take too long to sound appealing. They’re probably correct, but the effect of this system compounds. Teams select the “quick win” stories, entrenching the decisions they have made and optimising for cases that “look like” their existing solutions over radical divergence. All the while, this makes the big changes even harder, and those ideas and requests get pushed further out. So far out, that the customers realise they’re better off switching provider and getting a new solution to their big problems.
Don’t write more software, write software more.
The solution, glibly put, is aggressive refactoring. Whenever you learn something new about who your customers are and how they think about the problem, which is probably every day, encode that knowledge in your software. Too many software systems represent “the collection of features we give customers” out of which an architecture, or a design, emerges, when they should really represent “everything we know about what our customers are trying to do”, out of which features that support those customers’ needs emerge.
Unfortunately, existing approaches to software engineering tend to make refactoring difficult. This is because refactoring is seen as an engineering task, a thing that yields no value but that developers occasionally need to be given time to do because they seem to get angry if we don’t let them. Approaching refactoring in this way means that the software system lags behind our understanding of the situation our customers find themselves in. It also means that any change attempted is likely to be large, risky, and ultimately backed out or abandoned when it can’t be done in the small time box allotted.
Microservices, for all they help us solve other problems in software engineering, frequently entrench this inflexibility. The boundaries between services, if judiciously selected, help us with today’s division of concerns across our development team. They might not help us so much tomorrow, and may hinder the day after when a change has to be made across multiple services that, despite our goals, must be upgraded and deployed in lockstep.
When something hurts, do it more often.
Over the last few decades, software engineers have learned to prioritise those things that cause the most trouble, thereby reducing the pain. Is it a huge problem to merge changes? Practice continuous integration. Is planning and estimation difficult? Do it every week. Got deployment headaches? Deploy multiple times per day. Is it difficult to think about writing tests for your software? Write tests all the time, while you’re writing software.
Nanoservices is doing it more often for microservices, and for refactoring.
Microservices, a thousand times smaller.
Microservices are great for separating concerns, and for applying different deployment and scaling properties to different parts of the application. But if having those API boundaries imposes organisation constraints and limits on change, why not do even more of it? With Nanoservices, everything is a separate service, accessed asynchronously by sending messages. Now your application can make use of those multiple hyper-threaded cores in your developer laptops and deployment machines. And your whole system can scale elastically. Need extra computation for some complex regular expressions? Add more regular expression services on higher-spec VMs. Found a bottleneck in encrypting customer data? Scale your encryption services out horizontally, without changing anything else.
Imagine not that you have a microservice for a feature in your application, but that you have a service that looks after a couple of related data fields, the validation rule, and a transformation. That service is simple enough to understand that you can probably be very clear about how it should work, and write it without bugs. But let’s say that you talk to your customer and find out something new about the validation. In the past, you might have added some tests, patched the code, deployed a new version, and made a note somewhere to tidy up with a refactoring activity some day.
With nanoservices, you just write a new version of the service that incorporates the new idea, configure your system to use that service, and away you go. No downtime. No legacy code. No “engineering backlog”.
Want to learn more?
Contact the labrary to find out more and to plan nanoservice adoption in your team. Nanoservices are a realisation of the ideas in the manifesto OOP the Easy Way, so that’s a great place to get started.