Hacker News new | past | comments | ask | show | jobs | submit login
Microservices, the Unix Philosophy, and the Richardson Maturity Model (medium.com/chrstphrhrt)
88 points by nkurz on Feb 10, 2016 | hide | past | favorite | 49 comments



Any microservices discussion that does not take into account both infrastructure complexity and team size is incomplete. For small teams and small/not complex infrastructures, it's always a bad idea. The premium that is paid for switching to microservices is great indeed and it's paid in much slower development and deployment times. This happens as soon as you start breaking an application apart, so even with only a couple of "mini" services, much of the price has been paid.

Since the author doesn't mention either, I can't conclude anything, but it's very possible that the team he was advising didn't follow his advice because it was stupid and didn't take into account the above two factors.

See obligatory reading:

http://martinfowler.com/bliki/MonolithFirst.html

http://martinfowler.com/bliki/MicroservicePremium.html

EDIT: Spacing


The part I love about microservices, and something that I think doesn't get enough attention, is that is strongly enforces separation of concerns across micro-projects. API boundaries are very clear and very rigid, it takes very little effort or oversight to prevent leaking across them. Essentially, you are moving some of the software 'best-practices' up a level form modules to applications themselves. This means that you don't need to spend a lot of effort writing great, best-practices following, future-proof, software upfront for fear of collecting horrible technical debt down the road. Got a service that was rushed out the door and isn't maintainable anymore? Rewrite it! 2 weeks later you have erased a bunch of technical debt and since you kept the same API(s) in place, you have not affected the rest of the services that make up your application.

And that is the other thing that I think microservices are great at: They are pragmatically easier to change. Delving into a huge, old, debt-riddled monolithic codebase to make changes is hard and scary, and developers are less likely to want to make changes to monoliths because of that. Microservices are easy to understand because they are small, discrete systems with well understood interface contracts. Making changes to them without breaking the entire app is not scary because it is easier to throughly understand the implications of what is being changed.


I'm sort of playing devil's advocate here, but:

> Got a service that was rushed out the door and isn't maintainable anymore? Rewrite it! 2 weeks later you have erased a bunch of technical debt and since you kept the same API(s) in place, you have not affected the rest of the services that make up your application.

But that's also true of a monolith. Got a piece of your system that isn't maintainable any more. Rewrite it! 2 weeks later you have erased... you get the point.

I like the idea of microservices. I like the idea of the clean boundaries. As a developer, the separation seems correct to me. But, unless you're actually feeling the pain point of the monolith, it just seems like more work that falls into the category of YAGNI.

You also said this:

> Delving into a huge, old, debt-riddled monolithic codebase to make changes is hard and scary...

I mean, it's hard and scary if you don't have a high level of test coverage. If you have great coverage in the old monolith, you should be able to change pieces of it with confidence, no?


Yes, you can always rewrite parts of a monolith, what I am saying is that it is just way easier to do with the hard-boundaries and well-defined interface contracts of microservices. You know that as long as you live up to the interface contracts, any changes you make to the microservice app cannot adversely affect other microservices. Just think about how much of software development patterns and best practices are devoted to creating proper encapsulation and avoiding tight coupling; All of that is simply built-in when you use microservics.

Yes, of course if you have great test coverage making changes to a monolith can be easier, but good test coverage and debt-riddled don't often apply to the same codebase! Lets be honest, if you are happy with your monolith codebase then there is no reason to rewrite for microservices. It is for those projects that you are not happy with the monolith code, where maintenance of the code IS scary, that the conversion makes sense.


It's worth noting that a rushed service will likely have a rushed interface as well as a rushed implementation, at which he point the "clear and rigid" boundaries work against you as they're likely rigidly in the wrong place.

I think microservices have many benefits, but they can work against you when your discovering your model


Delving into a huge, old, debt-riddled microservice codebase to make changes is even more hard and scary, because now it's in multiple repositories and it has a lot more networking code.

If you can't write and maintain a decent quality monolith, what makes you think you can do it with a collection of microservices?


Author here, the project I was referring to is a massive unwieldy Scala project using the Play framework (nothing inherently wrong with those choices). It has 177k LOC. It is a matter of months old. It is complete spaghetti and it's impossible to find what does what without learning IDE tricks.

Guess I could have mentioned that. So actually my experience confirms the observations of Fowler about breaking up existing things rather than starting out with bunch of different services because fads.

That said, I would still start breaking up services that are orthogonal to one another in the same way as one might break up db tables. Iff the interface for writing new services is comprised entirely of a spec for endpoints and their corresponding data schemas. If instead you're thinking of how risky it would be to have a handful of separate services all with their own pile of fragmenting boilerplate to maintain, then yeah, I'd agree a monolith is better still if that's the only option.


Speaking from my own experience, if the team is not able to properly design modular software using the programming language features to write well defined APIs, hiding packages/modules/whatever behind RPC call isn't going to turn them into good API designers anyway.

Plus now they need to be good at distributed computing as well.


The writing style of the article was very dense. I would have liked it if you could have condensed out the the sogginess so the main points came through more clearly.


Could you expand on what "IDE tricks" you found necessary? They sound like something I need up my sleeve!


Don't forget....

- debugging

- networking

- service discovery

- distributed system fallacy list

I've become persuaded that generally microservices are an antipattern largely related to weak technical leadership. They bring incredible weight onto an organization, both technically and socially.


- latency

- eventual consistency of data

- ...


The cost of being able to efficiently deploy 5 or so services is probably 5x the cost of deploying your single Monolith (this can be a mostly manual process only doable by 1 or 2 people). However the cost of deploying 100 services is still only about 5x the cost of deploying the single Monolith.

There's certainly a size threshold below which a microservices approach hurts more than helps.


How do you figure that deploying 5 services vs 100 is the same cost?

Also how do you take into account maintenance and configuration of each individual service and tying them together as a whole?

From my experience, even at large enterprise scale, microservices have been a hinderance at the very least when compared to entrenched monoliths.

The effort to keep all the individual pieces working properly and harmoniously has never been worth it in any situation I've personally been in.


> How do you figure that deploying 5 services vs 100 is the same cost?

By using generalizable deployment configuration tools - ansible, chef puppet perhaps?. The configuration complexity would probably depend a lot on what exactly those services do. For example - if they are all tomcat containers - with standard integration endpoints then there would be diminishing cost associated with each additional node/service deployment.


Relevant: here's a reply I made to a similar question on the original post: https://medium.com/@chrstphrhrt/thanks-1b19b2e3ec09#.g493c33...


Maybe, but the increased development costs are incredibly high as soon as you move from a monolith, so much so that the deployment costs, no matter how large, will be dwarfed. For example, in my current setup, having a "mini" services architecture (typical monolith split up into three parts + supporting services) is probably costing us about 25% of our time. In other words, if we had a monolith, we'd be able to spend 3 extra months a year developing or refactoring that are now wasted in the slow development process that is caused slowly by the boundaries between applications.


You sure it's not the mainstream tools supporting microservices that are the problem? The E language, Fabric secure middleware, and even embedded on OKL4 with Camkes were straight forward. Small teams supported stuff like that because tools do almost all the work. It's like a modular, monolithic app with some parameters to tune and fault isolation.

Just seems like development, deployment, and support tooling could knock out issues people describe easily.


How is having microservices vs monolith increasing your development time? We have a lot of microservices, and I feel like having each piece of functionality silo'd with proper service boundaries only speeds up development.


Here's an example of equivalent operations:

Monolith:

* make db call

Microservices:

* send request

* receive request

* process request (equivalent to monolith db call above)

* send response back

* receive response

* decode response

Every single one of these steps needs to be debugged or at least tested every single time. Debugging itself becomes extremely difficult because you have to stop in one program and continue in another. You need a system for mocking data between the apps to reliably reproduce problems and debug them. There are problems that are silent between the two applications. You need to figure out a system for transactions. Or worse, distributed transactions. You have to deal with possibly sending out ridiculously huge amounts of data from app to app over a slow, unreliable network. You have to deal with service discovery. I could go on and on.

Every single one of these things adds complexity. For a small team or uncomplicated infrastructure (or both) this slows down the project immensely. That 25% that I'm quoting is from my current job and it's not overinflated. If you have enough people to handle it and your app is incredibly complex, then I have no doubt that your claims of speeding up development are true. If you're not at that scale/complexity, the opposite is true.


We are a small team working on something of moderate complexity.

We don't do a lot of request/response and most of our services interoperate asynchronously. The overall system is eventually consistent and we keep close tabs on the latency for changes propagating through the system. Different pieces have different latency requirements.

We don't have any services that exist solely to wrap a database -- in fact, most services read directly from databases. Updating them, however, is done by a single service.

We use S3 and Kafka as coordination points for large amounts of data.

We use DNS for service discovery.

Most information flows unidirectionally through the system. This + async everywhere means no race conditions.

The format of messages between systems changes very, very rarely. This is largely in part due to how we have segmented functionality -- most of the time, services just need to inform another service that some change or event has happened -- then the service itself contains all of the logic for what that means.


I don't really buy this.

In any modern language there's going to be pretty battle tested libraries out there to handle requests/response/decoding/encoding for you. This is stuff that is almost never need to be debugged.

If you have a small team and uncomplicated infrastructure, you're only hurting yourself by moving to microservices. However, if developer synchronization problems make core changes to your monolith difficult and block the work of a ton of people regularly, your monolith takes minutes to build or start up, and you're already having to shard the data your monolith deals with across many datastores, then there's no definitely no developer tax to switching to microservices.


Do you work on more than one in a given week? How do you test a feature which requires co-ordination between several of your services?


  > having a "mini" services architecture
  > is probably costing us about 25% of our time.
Wow, that's actually quit a bit. What would you estimate is taking most of that time?


> The cost of being able to efficiently deploy 5 or so services is probably 5x the cost of deploying your single Monolith

I don't know. As soon as you have reasonable Ansible templates for everything, it doesn't matter that much that you're deploying 1, 2, 5, or 50 services.


Can anyone add practical experience with the API spec tools. This is a new name for me.

I had a look at the websites for each of the three tools the article specifically mentions

    http://raml.org/
    http://swagger.io/
    https://apiblueprint.org/
I get the impression that these are code generating tools built from a specification file.

I have real world experience with two tools which fit that rough description. SOAP and google app-engine end-points.

I would unambiguously describe both as awful. Really horrible experiences with both. They tie your systems to large, and difficult to understand tool sets. SOAP wasn't obviously buggy, but app engine endpoints are unpredictable and the generated code produces some serious awkwardness.

I don't want to slap a label on these tools and dismiss them off-hand.

Anyone used them 'for the win'?


We've tried various ad-hoc approaches to documenting our APIs in the past.

When we switched to RAML (and json schema) for our API definitions, everything got a lot better.

RAML defines the resources (uris, parameters, media types, etc.) while json schema is used for more detailed information (what's the exact structure of the application/json response to a call to GET /jobs).

While there are theoretical gains in generating code from the RAML, we don't seek these out as its no big deal to hand-code Spring controllers, even a lot of them.

The real value for us is in having a single, well-documented source of truth about our APIs. Customers using the APIs, developers implementing them, testers testing them, trainers learning about them, all go to one place.

And RAML has some handy tools (like raml2html) to create human-readable documentation from the RAML.

We've also added our own tools to parse sample json responses against the json schema which have greatly aided us in finding discrepancies.

In short something like RAML (or probably blueprint or swagger too) is, I feel, essential if you have a lot of APIs and a lot of people, and if you have aspirations of an increasingly automated toolchain.

FWIW, there's a line of thought that RAML is at odds with hypermedia and HATEOAS. After all, why would anyone need a detailed API document, when clients should be getting information from the hypermedia responses of previous API calls? I don't agree with that, even if just because internally your development team still needs to build out all those APIs.


I've worked with API Blueprint as it seems to have the widest support across API tooling providers, along with a sane syntax. It's really nice to be able to spec out a blueprint and then simulate an API [1]. Also being able to run tests on the real API, but based on the API docs which describe expected behavior, has exposed many bugs [2].

I just recently watched the "Microservices Summit" from microservices.com/Datawire [3], and the most-repeated lesson from Netflix, Amazon, and Yelp was to design API-first, not data-first. As engineers we tend to design an API around our data -- but developers have to interact primarily with the API, and then with the data. With API spec tools we can quickly fake an API and try to interact with it, then find out what sucks and rewrite it, all before sinking time into the actual implementation. SendGrid does this, and their API has been a breeze to work with [4].

The last step is to use a client SDK generator like Apimatic [5] -- but that only works if the API actually conforms to the docs. It's probably difficult to get an entrenched codebase to switch to spec, but for any new API I wouldn't do it any other way.

[1]: https://github.com/localmed/api-mock or https://apiary.io

[2]: https://github.com/apiaryio/dredd

[3]: https://www.datawire.io/microservices-practitioner-summit-wr...

[4]: https://sendgrid.com/blog/quickly-prototype-apis-apiary/

[5]: https://apimatic.io


https://github.com/swagger-api/swagger-codegen#companiesproj... shows some companies using OpenAPI/Swagger and Swagger Codegen "for the win" - automatically generate API client and server stub given an OpenAPI/Swagger spec.

(We obtained permission from these companies to list them in the page)

Disclaimer: top contributor to Swagger Codegen


I've used RAML for the past six months, but only to generate documentation. It's pretty easy to learn. A RAML file is a lot cheaper to rewrite, so I write RAML first and figure out if the endpoint makes sense before I write the backend code manually.

I don't know of projects generating the actual server side code from RAML/Swagger/API Blueprint other than the OPs project.

Other than documentation, they are useful for generating mock servers for testing, API client libraries, and collections for postman/paw.


For generating API client or server side code, please try https://github.com/swagger-api/swagger-codegen and open a ticket via https://github.com/swagger-api/swagger-codegen/issues if you need any assistance.

Disclaimer: top contributor to swagger-codegen


Forgive my ignorance, but could someone explain how the tools mentioned above differ from things like:

https://msdn.microsoft.com/en-us/library/ff657795.aspx

Which I'd taken to be A BAD IDEA:

http://programmers.stackexchange.com/questions/124996/is-cod...


RAML and the others have a lot of value simply as the source of truth for your API definitions. You need to hold that somewhere - and probably share it with others - and a DSL specifically designed for documenting APIs is a good place. Where else would you hold it after all?

However, as developers we love to reach for the stars, so surely that's not enough? Surely we should be able to generate complete, working systems from the RAML (or whatever)! I would say that's a pipe-dream, just as it always has been, and just as it always will be. You might be able to generate a few stubs from RAML (or a UML diagram), but annoyingly, those are also the easiest things for a developer to just crank out anyway, and you'll be up to your ears in compiler soon enough.


We're working on the 'winning' part with http://ramses.tech :)

The projects you listed above are just spec formats that describe APIs. In the case of RAML there are companies out there including MuleSoft that are building generators and other kinds of services around it. I believe the same goes for Swagger and the others.


The one shortcoming of these approaches is that they are well-suited for local APIs but less so for network APIs, especially as you start to wire them together. For example, with network APIs you need to worry about stuff like rate limiting, and there's no way to express them.

That said, if you're trying to document a bunch of REST APIs, it's a very helpful thing to do. Just not a total solution :)


OT: More than 10 years ago, Jim Waldo [1] gave a great talk on SOA. I remember listening to an MP3 version of it then. I could find many references to the talk online now (eg [2]), but talk itself seems to have vanished off the web. Does anyone have the MP3 of the talk?

[1]: http://www.eecs.harvard.edu/~waldo/ . Also see a great paper he co-authored at http://www.eecs.harvard.edu/~waldo/Readings/waldo-94.pdf

[2]: http://www.gettingagile.com/2005/09/17/jim-waldos-talk-on-so...



Becoming encumbered by a monolithic centralized API that is at risk of reaching its maximum performance and hosting limits.

Comically wrong. You can just fire up more instances (aka horizontal scaling).

Microservices have their merits, but they're often the (misguided) Dev answer to Ops problems.


> You can just fire up more instances (aka horizontal scaling).

(sigh) I wish that were always doable.


Obviously no technology will give you infinite horizontal scaling for any sufficiently complex app.

There is more than one way to achieve scalability. Microservices is just one of the possible choices. It annoys me that developers increasingly see it as the only one.


> There is more than one way to achieve scalability. Microservices is just one of the possible choices. It annoys me that developers increasingly see it as the only one.

Agreed.

You can ensure a monolith is written such that it only performs expensive operations just in time. Then sit behind a load balancer and enjoy.

For example say you have 12 machines serving your REST API. If one particular service/route is impacting other services it is straightforward to deploy the entire application onto a dozen more machines and have the load balancer route the problematic service through to the new machines.

That won't get you google/facebook level scaling.... but neither will a basic microservices architecture.


There is more than one way to achieve scalability, but as far as I'm aware all of them beyond something more beyond just firing up more instances.


Designing an infrastructure which allows you to solve problems by firing up more instances is non-trivial work, beyond the skillset of many (otherwise apt) developers.


I'm not sure I disagree; but that seems to precisely refute your initial, bold claim in this thread.


My original wording was too concise, sorry.


"but as far as I'm aware all of them [require] something more beyond just firing up more instances."

Fixed that for me...


I like stuff like TFA that pulls in concepts from numerous sources even if all the pieces don't fit together perfectly. For instance, I doubt Leonard Richardson would be thrilled with the "Ramses Maturity Model" (we couldn't even coin a different initialism?) and the "Level 2.5" idea. Eschewing links runs counter to Richardson's (and Fielding's) vision, period. If one must know the form of representations ahead of time, one cannot consume them in a completely maintainable way. One wouldn't notice this within the organization, but rather at the edges where it must work with other organizations.

That said, Ramses and what it's described as doing is impressive.


Thanks for the great feedback, you're completely right. I have switched up the language a bit.

Regarding hypermedia: I really, really want to work with someone to add first class support for it. I don't think spec is a replacement but rather a compliment to hypermedia. The difference in benefit seeming to be static vs. dynamic client generation and I fully agree that dynamic ought to be the future. Right now static is easy to implement and still quite powerful despite requiring tradeoffs about things like needing to version releases of client libs rather than them being more durable in the dynamic scenario.


> Then there’s Level 3. It adds hypermedia links to the mix so that clients can dynamically browse resources à la HTML in a browser. This is where Ramses departs from all the awesome work that’s being done in the world of hypermedia.

This is the good ol' HATEOAS principle of REST: http://stackoverflow.com/a/9194545/449288




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: