The microservices cargo cult
stavros.io
stavros.io
The main advantage of microservices are in scaling and in reducing complexity of a big system but those advantages only make sense when you have enough traffic so that you have to scale or when your system has become complex enough to warrant microservices.
When first starting development, the most important thing is speed of development to get feedback from users as soon as possible. It's much faster to develop a clean, well optimized monolith than to spend a lot of time developing a whole bunch of micro services. And while thinking in term of microservices will help you to better conceptualize your software architecture, at this stage, you don't have all the informations needed to have a clear idea of what the final architecture will be and you'll often end up with microservices that are divided in suboptimal ways causing a lot of pain.
Doesn't that really depend on what the startup is doing? If your goal is a SaaS platform things might be different.
> ..you'll often end up with microservices that are divided in suboptimal ways causing a lot of pain.
Or one large monolith that is a mess. Really doesn't matter if you go with monolith or microservice architecture, you can screw up both ways.
Changing anything means changing half a dozen programs. Potentially in different languages.
Building any new feature into the program means you get to "walk the dependency tree", making sure no serialized stuff from a new version gets sent to an old version. Good luck with circular dependencies.
Related: deleting a field ... never going to happen. We're talking years of planning. Any field added ... ever ... has to be taken along for the ride for years. Oh and don't even think about deleting the code that interprets and supports the field for the same reason.
Also related: best of luck with the interactions between "oh we're not going to do that after all, sorry about asking you to already push that and update the database" and the "we really need to do this next thing right now" features. And the
Constant serialization overhead. People go overboard with this microservice crap and the result is that 99% of your program's cpu time is used putting objects to json and back (which is very expensive due to constant alloc'ing), and you have 10-100 times the normal memory overhead.
Microservices should be like optimization : build your program without using them and then figure out where they'd make sense.
yes I know, you can sort-of avoid it these days with cap'n proto and flatbuffers
This is true for ANY class you release. I'm not even talking about a service API: if you published a class, and it gets used, you should never delete or change fields in it. You might get away with adding fields to it, depending on various factors.
This is just the OCP; why is it a surprise?
If you serialise / deserialise you are probably coupled to an underlying type. Modify it instead of extending it and you have pain.
If you parse the raw message, or serialise into a hash, or dynamic type then you have one place to make amends (your parser). Taking things further you can version your messages and map them to versions of parsers, as such you'd never violate OCP (or at least simply mitigate).
Granted all of which is additional complexity compared to your stereotypical ball of mud / monolith that'll let you refactor field name changes etc to your hearts content. Obviously ball of mud approach has draw backs too...
1) field is no longer important, gets replaced
2) write a getter that still gets the value of it, but delete the field itself.
--- this is where it stops for C++
3) use the "inline function" refactoring on your entire codebase
Done/done. You don't even have to rewrite the tests, in fact it might be better not to.
(Of course getters and setters are somewhat verbose in most cases. Object pascal has the perfect solution though)
Same with monoliths, except it is often a worse experience. At least with microservices I know the interface is all I have to worry about. In monoliths of any size inevitably someone has reached into parts of the program they shouldn't have just to get something 'done quickly'. And this is one of the main benefits of microservices - enforcing the interface boundaries.
> Related: deleting a field ... never going to happen. We're talking years of planning. Any field added ... ever ... has to be taken along for the ride for years. Oh and don't even think about deleting the code that interprets and supports the field for the same reason.
That's just poor design and happens just as much in monoliths. The db is the almost always the challenge when removing a field. I could argue that microservices make it easier since the service providing access to that field could remove it from the db and then dummy it out until clients are updated. Also, why wouldn't someone remove the field from all the clients when removing it from the supplier?
With that said, I agree that microservices should be something that happens organically from a monolith. Think about an amoeba that reaches a certain size and only then do parts split off. I also think there is some ambiguity to what constitutes a microservice. I'm sure my idea of proper granularity is different from others.
You wrap your code inside a reusable library if you want to encapsulate it and expose only a small public interface. You don't start a new service for that.
Once there are more than a handful of people working on anything no one person understands the details of the whole. Smaller, specialized services can at least act as and enforce those boundaries as upstream comments said.
Until you have so many services, that you also need someone that understands the details of all the different services interacting.
Really, if you can't control your developers from leaking through separative concerns, then they'll do it in any architecture. Monolithic or microservices-based.
Digging down through 7 dll projects in one solution, then 11 in another to add a small feature that should take 10 minutes taking 4 days isn't so much fun... same would go for 30+ service projects...
I'm also not big on starting with smaller/micro services either.. or ORM, or DI/IoC for that matter... Build the monolith and break pieces off as needed. I'm fine with that... I was saying it's easier to herd cats when they're not in the same space as eachother.
Wouldn't this only be a problem if you broke up your service along the wrong lines?
I don't think it matters how you split it - if the team working on one module prefers ruby and the team working on another requires python for any 'serious' work you need programmers who understand both to get the 'big picture' of your codebase now. That one guy who writes all his stuff in PERL can write his plugin that way because it just interacts via API. :O
Now how do you debug something?
(1) Easy scaling allow for worse coding (and therefore quicker developement).
For "webscale" you're right, and if you can run it all on a single cheap server, then no need to worry about it - small servers are so cheap, the cost per month is insignificant relative to, say, ramen cost per month.
But what if you write your app incredibly inefficiently? Perhaps because you develop it quickly; you're not highly skilled (or not even a "developer"); it's difficult to optimise. It's very slow, seconds per request. That's fine for getting started, but you need to scale it far before it gets to "webscale".
You could just upgrade to a better server; but at some point, the costs becomes prohibitive. The key thing about microservices is that they should be cheaper (because vendors can better utilise capacity; and customers only buy what they need, when they need it).
(2). You don't have to architect the whole project for microservices from the get go.
e.g. If there's a single resource-heavy function, factor it out into a microservice. Your main code becomes super cheap to host; the microservice only uses expensive resources as needed: when and if it runs.
===
At the moment, microservices are a pain to set up and manage. But with tools, it will become very easy to factor out a module into a microservice.
That is, assuming that code is already a separate module - microservices aren't magic pixie dust!
Major problems I've seen are: per transaction performance sucks due to the network or IPC channels, development friction, logical complexity, infrastructure complexity, managing contracts between services, debugging failures, monitoring performance, bootstrapping new staff and the biggest of the lot: headspace.
If you want to succeed, at least in the short term, just keep your monolith tight and fast and without sprawling infrastructure requirements. Single machine, single process, single storage engine (or database), single messaging system. Then scale that to multiple instances. If your site deployment requires at least 20 machines due to sprawl, you're going to be even more screwed when you throw microservices at it, not less. If your application is incredibly complex, it's not going to work either. The problem domain needs to be small and easy to consider as it's difficult to cleanly extract a chunk of your average monolith into a standalone concern.
There are also people with technical authority in many companies who blindly follow the latest fad without real consideration of suitability, risk assessment or accountability. If someone starts waving microservices, AWS and everything else around, they need to fight their position and everyone needs to assume that isn't the default end game.
> it's orders of magnitude better to deploy lots of smaller identical monoliths than it is to try and build and deploy lots of services and manage the contracts and complexity between them.
This article from 2003 by Martin Fowler is exactly about that: http://www.drdobbs.com/errant-architectures/184414966I'd probably start off any project with a monolith-first approach, and if I find parts that can be cleanly isolated that require minimal cross-communication I'll consider splitting it out; or at least keep it mind as I implement an initial prototype.
Perhaps I'm being selfish because having all your data in one place (esp with an ORM on top) makes my job of automation really easy. E.g. to generate monitoring configuration only for servers in accounts that are not suspended, that are provisioned, that have a public IP, that are not in the Blackhole table etc is really easy with JOINs.
Imagine that each Python module runs as a microservice. For many modules this would lead to huge performance degradation, for example a regexp module can be called thousands times per second, the running time of a call is usually short and replacing an in-process call with a network call will give 100-1000x slowdown.
But if you take a different use case of the same module - complex regexps running on large texts, potentially causing out-of-memory errors, then packing the module into a microservice can make sense - separate processes can have large caches, an out-of-memory error terminates an instance of a microservice only and not the calling process.
Generally I think the advice should be to always use source code modules in the first place, and create microservices using these modules for specific use cases only involving runtime needs like caching, fault tolerance, scalability.
This is absolutely irrelevant. If your call budget is 400ms then extra 4ms that it takes fetching a data from a micro service is negligible. Make 400ms 4ms and you are done.
Your database is a service. You can't use multiple dbs depending on the situation.
If it can be replaced by multiple instances of the same module than yes, creating a microservice is probably stupid
For example your regex, could be given 20ms to run and use a maximum of 8Mb of heap before it is interrupted. This can happen on a single thread and drop back to the caller if an exception is thrown. I'd love to see a language feature which defines the maximum stack and heap for a particular scope i.e. in C#:
Breaker.Heap(8.MiB(), () => {
Breaker.Time(200.Milliseconds(), () => {
// risky operation
});
});
(we already do the time breaker, but not the heap)Edit: correct calling convention.
Edit 2: add missing extension method brackets
To my knowledge, it can't be implemented on Linux + Java or Python (a thread can't be terminated from outside, and some syscalls involve a whole process).
Stopping the thread can still leave some of the stuff in indeterminate state if the thread has some resources it needs to release manually.
The time breaker is actually quite complicated. It is a wrapper that sets up some global parameters on the thread for timeouts on async/await calls and handles the timeout conditions. It integrates with our own async wrappers for external http calls, message delivery, query execution etc. It only enforces that all aggregate async calls will complete or fail by the end of the timeout period. Realistically this is usually around the 500-800ms space as load spikes can break everything otherwise.
By themselves, the overloads for integers like that is pretty neat, never seen that before. I can actually think of several dozen places I do things like TimeSpan.FromSeconds(x) where that could enhance readability.
Code:
public static class IntegerExtensions
{
public static TimeSpan Minutes(this int val)
{
return TimeSpan.FromMinutes(val);
}
}
Usage examples I can think of: var eightMinutes = 8.Minutes();
var tenMinutes = 8.Minutes() + 2.Minutes();
DoSomethingEvery(8.Minutes());In combination with operator overloads and implicit casts and you have some really powerful tools for building readable APIs :)
Yes yes yes ! If your architecture and data model are funked up, then it doesn't matter how you implement them - you are screwed. On the other hand, proper modeling with separation-of-concern and well-defined interfaces will let you implement as anything you need, be it microservices or function calls inside a monolith.
Replace Microservices with Microkernel and you can read the Torvalds-Tannebaum debate instead [1]
To me, components and services are two completely separate abstractions. A component is a collection of code -- a module -- that can be duplicated and used in numerous different systems (much like a 5 ohm resistor). Updating a module in one system doesn't affect any other external system. Services on the other hand I think of as multi-tenant systems that are used to provide common functionality that is likely to change.
There are midway options though. Even the lowly batch job is a good way to get some of the decoupling without having to go "all-in". I find batch jobs and message queues give me 80% of the benefit of "microservices" with only 5% of the pain.
In fact someone needs to write an article on "levels" of "microserviceness", (which certainly has multiple dimensions and branches) and point out the benefits and drawbacks of each level.
Of course the end game being: "a Docker container for each line of code."
I think I might literally have a "SOA Maturity Model" slide laying around somewhere from my mid-2000's consulting career.
Let me know if you need it, ironically or not.
But the parallels are eerie. Someone gives a nomenclature to a style of architecture that many people were already using (just last week I heard that Netflix "invented" microservices), and suddenly the attention of the industry pivots. Technologies will be invented to ease adoption (yay!), but people will forget that this architectural style is one part of a solution for a particular type of problem -- it is not a solution for every problem, nor does adopting it mean all of your other problems will go away.
But consultancies will rise, debates will rage and conflicts over what truly constitutes a microservice will ensue.
So true. I feel like this has always been an issue in the "services" world. It's hard to nail people down on a definition that's both accurate and prescriptive. Either you end up with a definition that is so generic as to be unhelpful, or you come up with narrowly defined definitions that miss important use cases. I've decided that "services" is a word like jazz or porn or art: you know them when you see them.
My only wish is that we could, as an industry, find a way to better bootstrap our systems. We have a tendency to launch products / companies around bad ways of doing things and then spend a not inconsequential sum of money retrofitting everything.
It would be great to see tools and framework built from the ground up to address both developer productivity and scalability. It too often feels like achieving scalability comes at the cost of developer productivity... perhaps because by the time you need to scale, you've got enough resources to hire tons more developers and have them engage in menial tasks.
What's different this time round is that microservice hosting should be much \cheaper\.
When you're cheap enough, all sins are forgiven.
Yes, this is the surest way for developers to guarantee the code itself is always bug-free. Everything becomes "just an ops problem".
2017 predictions: buzzy job title: Ops-Dev. Labview-style graphical programming http://www.ni.com/newsletter/app/largeimage?lang=en&imageurl... becomes the new hotness. Amazon and Google introduce new graphical programming workflow coordination services for lambda. Nothing ever gets finished.
(2018 predictions: google discontinues this service)
(2019 predictions: every machine instruction now belongs to its own docker package; the successor to Kubernates is marketed as a distributed asynchronous virtual CPU. Data centers begin requiring small nuclear stations to generate the electricity to power them. Still nothing gets finished).
(2020 predictions: someone rediscovers this "heroku" thing; things start getting done again. Hot new industry: nuclear waste disposal)
(2119 prediction: nuclear stations powering 2019's data centers are still running at full power but servers are all dormant; nobody wants to turn off a server "just in case").
Those things are always sold to CIO types based on the idea that "you won't need developers" and then you end up with some eldritch graphical abomination where being being able to visualise things just makes it worse....
Draw a rough diagram and then write your code - fine, generate a diagram from code - fine, generate code from a diagram - nightmare.
Unfortunately it's a bit late for me to include this in the existing article, but it's a good topic for a next article. You will generally have a much easier time splitting tasks off and running them on workers than full-blown, standalone services (because the MQ can retry the tasks, they can be idempotent, you can have different timeouts, etc).
The usual definition of a microservice is a stricter subset of a task, in general.
Current client is a photobooth. Photos all can be shared from client app and web, each "share" is just a REST post containing "shareType:string" and "meta:json"; all share types (email, mms, twitter, etc) go through the same endpoint. Web api simply adds these to message queue for the corresponding "shareType" job to process. This way new types of sharing options can be added just by changing the client and adding the new batch worker; no updates to the web/api server app itself are necessary.
Proper "microservices" are usually described as being directly coupled to each other. They're just the new hotness so everyone wants a piece.
As most of the comments are saying, choose the shoe that fits.
I'm pretty sure the Docker, Kubernetes, and CoreOS parts are unnecessary (as, IIRC, microservices started being discussed before those tools were available -- those tools were driven in part by microservices, but aren't essential to the model.)
HTTP is, likewise, unnecessary: its the most obvious protocol choice, but there is no reason that microservices must be HTTP-based -- you could have a set of microservices using just about any protocol you want.
https://en.wikipedia.org/wiki/Enterprise_service_bus
Were are my mainframes ? I think I'll need them soon.
Most of the gotchas the article mentions aren't logical consequences of decomposing into smaller services at all. You don't have to have different data stores for each service. You don't need to "marshal data" between services. If a service needs to call a service it's just a client like any other client, so if we want to call standard http request/response handling "marshaling" I guess it will sound more complex and scary. Breaking a monolithic app into smaller pieces doesn't increase complexity, it reduces it. And to the extent you have more things to monitor that probably means you can now monitor and control things that were more or less invisible outside the log data in the monolithic architecture.
More importantly, decomposing a problem into logically related areas of functionality that can execute separately allows you to make the most efficient use of compute resources, and it is consistent with the idea of favoring multi-processing over multi-threading. In almost every way groups of simpler things collaborating makes much more sense than large complicated things that do all. It's only when we create these Knights in shining armor that people start feeling like they have to be knocked off their horses. Use the tools and techniques that make sense.
- "slowdowns on the order of 1000%"
- " bunch of code necessary to marshal/unmarshal data [...] there are always dragons in there.
And also problems of versioning, data integrity, etc.
I've had those problems in a microservices architecture. That's things that are solved by protobuf[0]. Your servers exchange small, efficient structured data and you get tons of other benefits ({un,}marshaling for free, integrity, versioning, ...).
Potential downside: a language you want to use having no protobuf API.
Finally, I see another downside to the microservices architecture: it may be decided that the smaller, decoupled code bases should be stored in multiple CVS repos. Which turns into a nightmare: a single bugfix may span across multiple repos and there is no clean built-in way to links commits across them, you still should sync the interfaces (e.g. with git submodules), etc. This is a thing I've witnessed firsthand, and proposals to merge the repos were dismissed since "We [were] using a microservices architecture". Yes, it's a mistaken implementation of the microservices paradigm, but it still happens.
edit: I recommend protobuf not by preference over other equivalent solutions, but because it's the only one I know and have used. Alternatives are evoked below.
Just use zerorpc. It's more reliable than zeromq + protobuffs and it comes with a bunch of freebies, like built in heartbeats, streamed responses, etc.
I'm talking about a function call overhead only so if the actual processing takes more than a few milliseconds it stops being important.
[0] http://www.eecs.berkeley.edu/~rcs/research/interactive_laten...
They still marshall/unmarshall but they do a better job hiding it from you.
The network is still unreliable, insecure, slow, etc. and so will still have many of the same failure modes as HTTP. A better Corba is still Corba... Whether you call it XMLRPC, RESTful, protobufs, Thrift, Hessian or what have you.
And goddamn I hate the optionality of all fields that is "best practice" with protobufs. It makes processing any somewhat complicated data structure a PITA.
A modular monolith application is what people have been writing since people thought up the notion of modules. Enforce proper discipline when building your app out and you won't need these physical walls between your functional areas.
I'm currently reading SICP, and the notion of using "block structure" in Lisp to compartmentalize and encapsulate functional areas of code is introduced in Chapter 1.
Get the basic stuff right before you start introducing complex systems to split up your software.
I've used a Twitter API client (works fine) that implements itself across 7 separate libraries. All same project, they all work together, and they aren't used in any other system. Just separated things out for the fun of it. One library for Twitter client "Factories". One for Twitter "Credentials". Another for Twitter client "Security". Zero benefit to the user or to the project. But it certainly makes things seem more important, eh?
edit: And it seems like someone already thought of that and wrote a module for it ;) https://www.npmjs.com/package/npm-autolink
[0] http://dustycloud.org/blog/javascript-packaging-dystopia/
I can live with that. The worst fate would be having to modify what npm does (replacing the package fetch with building from source). Given that npm is licensed under the artistic license, thats not half as bad as it sounds.
I do agree that there is a lot of unnecessary complexity introduced by today's JS build tools such as grunt and gulp. But npm isn't to blame about that - its only sin is creating and nesting duplicate dependencies, something which is already fixed by npm 3 (completely flat node_modules unless there are unavoidable dependency conflicts). The solution here is to avoid bloated packages such as grunt and gulp that try to install everything plus the world. (coffee-script grunt? seriously?)
One thing npm could do is separate buildDependencies from devDependencies. There are a lot of tools listed there that only pertain to testing, and testing a serious client-side JS library such as jQuery properly is a hard and complex endeavour.
Also, I can't help but notice the irony of complaining about the complexity of web application build tools given the complexity hell that are autotools :)
Also, a package manager should not be a build tool. A build system should never require a particular package manager to be present. It's okay to say "you can optionally use this package manager to get all the dependencies needed to build", but it's not cool to say "you need this package manager in order to build."
I guess we may've gotten a little too happy with the centralised proprietary cloudy source distribution channel with a single point of failure...
However, if you're a distributed team (maybe across timezones), quick discussions are difficult and 'costly', then microservices might worth the effort. Managing the deployment and operations is more difficult but sometimes much less coordination is needed when people communicate through APIs and not Skype and Slack.
This is really useful to reduce the level of required of interaction (and pressure) between teams.
If you have teams that communicate badly, you'll need people specialized in deployment and assigning issues to them. That's not a good situation anyway, but it's the less worse of them.
There are obviously patterns for doing this in monoliths (or, say, mobile clients - another type of monolith), but at some point you are bound by the dependencies inherent in a single runtime.
To apply microservices effectively, you should first build the monolith, modularizing at the source code level and adding choking points as needed. Over time, microservices will naturally roll off the monolith not unlike boulders rolling off mountains after rain or earthquake. Don't go dynamiting in anticipation.
If you want to work in a sexy new technology, but you need to develop experience in that new stuff to be marketable it is totally understandable to try to build up skills by forcing the implementation of over-sized solutions.
In other words, many employers aren't willing to take on folks if they don't have the requisite experience on some new stack and that compels folks to gain that experience anyway they can, including "cargo-culting" stuff that isn't necessary just for the experience gain.
The advice I give them is "do whatever you want in your house (or your side project), but critically evaluate your business needs and only use what makes sense for your business".
Personally, I have a very low-traffic guinea pig side-project that I like working on, and I just try every new thing there.
Generally when I hear somebody dismiss a suggestion because they think its real purpose is career progression. I am immediately suspicious of the accuser.
I love the microservices concept, but fair warning: as bad as OO has gotten over the past 20-30 years, microservices promise to be even uglier.
Why? Because not only are you mucking around in the code, you're also mucking around in how everything connects to everything else in your cloud.
Just like we saw vendors come out with click-and-drag ways to create new classes, now we're seeing vendors start to sell "pre-finished" microservices. Get the disk out of the box, boot it up, fill out a couple of forms, and voila! Now you have microservices.
That worries the living crap out of me because microservices are the architecture of the future. You just can't get from here to there using a magic bullet. Learn you some pure FP, make everything composable using the Unix Philosophy, and keep your LOC to a bare minimum. Toss off every damn thing you don't need.
As much as I know they are the way forward, I have a bad feeling that consultants will have plenty of billable time coming up straightening out a lot of messes.
Too many microservices is a complexity mess, too little means you have a monolith that is hard to iterate on.
If one heavy part of your rails app takes up 90% of the processing time, there is nothing wrong with just getting a bigger machine for the whole app. The bigger CPU/memory/whatever will be spent on the heavy part and the rest will be normal.
For most business, scaling is not a problem - they can just get bigger machines. Having to re-implement transactions across your microservice architecture really is a problem. Very often transactions need to cross microservice boundaries and that really requires a lot of thought
Yes, sure, but that's the resources spent right. Not the monolithic spaghetti crap you waste time on trying to figure out what went wrong - 99% businesses everyday activity
http://paulhammant.com/2011/11/29/cookie-cutter-scaling/
Moreover, unless you're doing something intrinsically computationally expensive (video transcoding or whatever), or you've screwed up, your bottleneck will be in the database anyway. Scaling the database looks exactly the same for monoliths and microservices: you can scale up, scale out, or split.
As for scale: are you suggesting that having extra code executing and network calls being made in your system makes it more scalable, rather than less?
Ten billion requsts a month is 3805 requests per second on average; i'd guess that means 10 000 requests per second in the peaks (correct me if i'm wrong!). Is this considered challenging scale today? I'd buy four DL380s and call it done.
No, it wouldn't allow us to be as fast. Most of our services are under <200LOCs (not a policy, just happens to be the point where people seem to split things out). The idea is that any service can be rewritten completely in a few days.
There are no tie ins to any platform, compiler version, syntax, or language. This might sound like chaos, but it's a huge productivity gain, as I feel full ownership over features I write. Naturally, we aim for good docs and code coverage, and use continuous deployment and integration tools to keep everything green.
As for scale, any microservice can be run across any number of instances without having to scale up the entire platform. This allows us to identify hot areas and deal with them effectively.
We don't use network calls (well, not HTTP or TCP) to communicate between services. Services themselves are pretty transport-independent and work well over tcp, but NATS is the transport of choice at the moment for inter-service communication.
IMHO, this is the biggest problem with microservices: "Transactions" are not available in a microservice environment. You'll have to work really hard to get anything that comes close.
Use a proper framework like Symfony (or if, like many people, all you want is a CMS, Drupal) supporting MySQL master-slave or multi-master replication and separation of web frontend and file hosting, host it on AWS (or plain old dedicated servers), put in Cloudflare if you're scared of DDoS kids, and be done. If you need SSO use either provided SSO plugins or an LDAP backend if the SSO is only required for various platforms provided by you.
Said architecture can be built and run on a single server and if you're dealing with spikes you just spin up a couple frontend servers and be done.
I have seen people shipping SAP to small brick-and-mortar stores with a tiny webshop...
Keep it all in one deployable artefact, in-process for as long as you possibly can. Use an in-proc message bus first, don't dive into Rabbit until you know you need it. As soon as you require that infrastructure cost for http, mq, monitoring a ballooning of boxes / VMs, deployment complications you'll notice the spike in operational expenditure.
Grow you architecture organically.
If you have to work with different people, you need a way to minimize dependencies between them.
Also, the more encapsulated things are, the less the starting skill of a person matters. You just need people who get things done. Later you can switch out the bad modules easily. Which is a huge economic factor.
I can't count the hours I spent with fixing horrible monoliths and the years it took to replace them.
But if there is a horrible microservice, you can do this in a fraction of time.
It's amazing how much a team of one can do if you don't saddle said team with arbitrary complexity such as a microservices architecture. Maybe you'll need to scale to that level one day. But you'll definitely want to ship. One guy and a sane architecture can do that.
They can be pretty nice for multi-tenanted development environments. Sure, you could use any of the other isolation techniques, but being able to provide an environment that can be started quickly (and somewhat easily depending on the rest of the services required). Not to mention that the popularity of container systems and their ease in understanding (Dockerfile vs RPM spec) means that other people can hack away at the dev environment without having to know the ins and outs of building proper packages (although they should learn).
Now, for a production environment, I would never move to a microservices architecture for the reasons listed in the article and my own dislike for adding overhead and complexity to solve "issues" that can be easily dealt with using tools that have existed for years (proper packaging with dependencies etc..).
A well-designed micro service architecture is modular in that each micro service is basically a nice wrapper around either a query or an update. But you can organize your application into an API of queries and updates without micro services.
To be honest, if you don't at least intuitively understand this, you have no business architecting a production system large enough that this matters.
I would suggest using functional styles wherever possible, plenty of isolated unit testable code, and a hexagonal architecture http://alistair.cockburn.us/Hexagonal+architecture that pushes all the I/O, mutation, side effects, etc. to the very boundary of your code. Also see Gary Bernhardt's "Boundaries" talk for more interesting thought in that vein https://www.youtube.com/watch?v=yTkzNHF6rMs
In an asynchronous RPC scenario, does Microservice A listen for the appropriate response message from Microservice B before continuing work on Request X99? Does it respond to all messages in the appropriate order? What happens in a cascading failure scenario when the back-end system Microservice B relies on is taking too long due to bad hardware/burst traffic/DDOS/resource contention?
Do you have tools that can analyze your program for critical sections where you need explicit locking and ordering mechanisms? Do you have analysis tools that provide guarantees that your fancy distributed architecture is complete/correct?
These are just a sample of the things OpenStack has to think about -- a micro-service architecture for managing, orchestrating, and authenticating access to data-center resources. It's a hard, hard problem and an on-going effort by thousands of well-paid engineers across the globe to get right.
I have no doubt that a small team of talented developers could stand up a system of APIs around their core services to get a system running. However I can guarantee that they will be making huge trade-offs in terms of correctness and reliability.
At least with a monolith (is that a pejorative?) application you do have tools to analyze and debug your code that work well and have been battle-tested for a couple of decades. I suspect you would produce fewer bugs if you were constrained for developer talent and time.
You have challenges, though. One of them is when implementing micro services you need a cultural change in your business to be able to adapt to the change. You need to deal with more complex architecture, you need to implement your own solution to deal with the architecture, spend time defining a dev ops culture if there is none, ...
Businesses are usually pretty different between others so you can not expect to have the same solution to deal with your problems (For example, using Netflix approach as a silver-bullet solution).
I've heard so many times the concept "micro services" as the goal as same as "big data" as the solution. Again, we should analyze what is our problem and what we want to solve before selling the new shiny thing and making things over complicated.
One benefit I haven't seen mentioned yet: microservices are effective at reducing the mental "page size" when working on any particular part of the system.
> You immediately increase the things your servers have to do tenfold.
Really? It's ten times as much work to implement microservices?
> Personally, I’ve seen slowdowns on the order of 1000% when moving to microservices (yes, ten times slower).
Then you implemented your microservices wrong.
I think that the author's understanding of the goals and purposes of microservices is maybe a bit misguided. Microservices are about front-loading scaling problems, not about having a clean architecture or smaller codebase. If you never need to scale, you don't need microservices (but you're probably wrong).
The flowchart at the end of the post really underscores for me that this author's argument is not genuine. He holds up this shibboleth of a "monolithic" architecture, something that doesn't really exist in 2015.
No, it says the _servers_ have to do tenfold more work, not _you_ to implement them. Whether that's correct or not is another discussion.
We also separated the main backend/API codebase from the frontend mostly because the frontend devs work prefer to work within the Node ecosystem instead of Python/Django and so that we don't have to think too much about synchronizing deployments. The tests for the backend code take quite long to run as well compared to the frontend tests, so having this separation is nice for the frontend devs that way too.
What I some times would like to have better infrastructure support for though is throwaway prototypes/projects that can live in their own codebases and have access to all the regular databases, blob storage and so on, as well as databases that are private to the prototype that I can do whatever with with no risk of doing something bad to the important databases/storage.
I would also like these prototypes to be able to register themselves with the load balancer to take care of everything under `/halloween-experiment/` for example and have the load balancer add headers like `X-UserEmail`, `X-UserID`, `X-IsEmployee`, and so on so that I don't have to implement authentication/authorization in every prototype.
Today these types of prototypes need to live next to the "important" code so that they can use the same CI pipeline and easily be made public or visible to employees and use real data.
I'm following projects like https://getkong.org/ with interest, and together with everything happening around Docker such as EC2 container service or Kubernetes, as well as projects for service discover/configuration like etcd or Consul, it feels like we're getting there. There are just so many projects to keep track of, and you need to figure out how to make them all part of your CI pipeline. :)
Just because one extreme isn't working for you doesn't automatically mean the other extreme is the right solution.
The author focuses on microservices, however, I think there is a larger point to be made. It is not that some particular architectural pattern is bad or good, it's that when you don't fully consider the requirements of your application and apply some pattern or technology just because it's the hot item this week you are going to end up with problems. This has less to do with microservices, in my experience, and more to do with less technical managers making decisions for a project when they don't fully understand.
Maybe we should call it MDDI?
Every time I think of the mess that it will cause to break up things to microservices, I'm glad we aren't doing it- yet. When the time comes, we'll roll out to services as-needed, but that day isn't today.
I have been meaning to see if there are microservice like frameworks for Haskell similar to Hystrix (which is what we use).
Almost all of the swathe of microservices we've developed internally are general-purpose. We've built a dozen or more user-facing apps on top of them. If I wanted to build a new app today, I would typically sit down and write a Node + React app, configure some backends, and I'd be done. I don't need to write a new back end because I can just call our existing services.
If you look at what a modern web app is, most apps these days are actually stupidly similar. They typically need things like:
* User accounts
* Authorization with existing OAuth providers (e.g. Facebook)
* Some kind of database to store and search structured content
* Notifications (email, text, push)
* Storing images or video
* Syncing data from external sources
* Analytics
We have generalized, reusable microservices that do all of this.
Let's say I want to build a HN-type link aggregator with comments. I will use our document store to store the links and the comments in a nice hierarchical structure. I will use our login microservice that mediates between an identity data model and an OAuth account registry. I can use our tiny microservice devoted to recording up-/downvotes. I can use our analytics backend to record high-level events on every UI interaction.
I can write this without a single new line of backend code.
This ability to "pick and mix" functionality you need is the real, largely undiscovered beauty of microservices, in my opinion. It's the same idea that makes AWS attractive to many people; you're building on the foundation of thousands and thousands of work and reusing it.
We just whipped up a new site recently where 95% of the work was purely on the UI, since all the backend parts already existed. The remaining 5% was just code to get data to the system from a third-party source, plus some configuration.
Reusability requires that you plan every microservices to be flexible and multitenant from day one. It's a challenge, but not actually a big one.
Is it possible to do this monolithically? Sure. I would be afraid of touching such a beast. We have very few issues with code devolving into "legacy", for example; the strict shared-nothing APIs ensure that half-baked, client-specific hacks don't sneak into the codebase. If anything messy happens, it happens in the client app, and that's where it should happen. Eventually you'll throw the app away, but the backends remain.
I think what's frustrating is the lack of support in moving from a monolith to a microservice architecture. I haven't built a lot of them myself, but it feels like you're rolling your own framework/architecture whenever you need to make the transition. Is that anyone else's experience, or is it just not possible to codify best practices?
That's valid for functions, state, api and service stores.
When does splitting a service add enough value that it is worth the cost of performance and added complexity?
Simply put, you split a microservice when you need to split teams.
Microservices aren't a solution to a technical problem, they're a solution to a social/organization problem (described by Conway's law).
In the summer of 2013 I was working at Timeout.com and we were trying to reinvent the architecture of the site. Timeout.com had spent several years using the PHP framework Symfony to build a massive monolithic CMS, and the thing was a disaster. It was shockingly slow. If you ssh'ed to inside the datacenter and then tested the response time of the system, under ideal conditions, from one computer in the data center to another computer in the data center, then the average response time was 10 seconds!
This lead to a long internal debate. I advocated for what I called "An architecture of small apps", because at that time none of us had ever heard the word "microservices". I did not hear that word until March of 2014, when Martin Fowler wrote his essay:
http://martinfowler.com/articles/microservices.html
But back in the summer of 2013, with permission, I published the whole internal debate that we had had at Timeout.com:
http://www.smashcompany.com/technology/an-architecture-of-sm...
You will notice that you don't see the word "Docker" in my essay, nor do you see it in Martin Fowler's essay. And in my essay, I suggest we use ZeroMQ to bind our apps together.
But 2 years after we had our internal debate, I've noticed that more and more people now associate "microservices" with a very specific set of implementation details: Docker, Kubernates, HTTP and Service Discovery.
I acknowledge that these 4 technologies can be combined in very powerful ways. I currently work at the startup incubator run by NYU, and I get to eavesdrop on what the folks at lsq.io doing, since they sit next to me. And I get that Pelly is a frighteningly smart guy doing extremely cutting-edge stuff. I totally admire everything they are doing.
However, I personally feel that I'm following a microservices strategy, and yet what I'm building is still a lot like what I described in my essay of 2013.
July 30th, 2013 http://www.smashcompany.com/technology/an-architecture-of-sm...
fight the future