[HN Gopher] Write libraries instead of services, where possible
___________________________________________________________________
Write libraries instead of services, where possible
Author : mooreds
Score : 244 points
Date : 2023-11-23 14:21 UTC (8 hours ago)
(HTM) web link (catern.com)
(TXT) w3m dump (catern.com)
| mooreds wrote:
| I think this sentiment is a partial cause of the trend towards
| self-hostable, downloadable software too.
|
| The customer has a cost when they operate a library instead of
| consume a service, no doubt. They also get more control (no
| surprise upgrades, availability is their responsibility) and
| assurances (no worries about the service suddenly being end of
| lifed).
| taneq wrote:
| When you use a library from an outside source, if you update
| that library, it might break your build.
|
| When you use an outside service, they might break your system
| at a time of their choosing.
| mooreds wrote:
| Exactly. It'll break sometime (it's software). So when do you
| want it to break? And is the benefit of controlling/planning
| the timing of that breakage worth the cost of operating the
| software?
|
| As always, it depends. Questions I'd ask:
|
| * How critical is the software to your application's proper
| functioning?
|
| * How big is the team?
|
| * Who are your customers and what are customer expectations
| around your application's proper functioning?
|
| * How often does the library change?
|
| * What expectations does a service set around backwards
| compatibility? What commitments are made?
| nijave wrote:
| >When you use an outside service, they might break your
| system at a time of their choosing.
|
| Well sure, but if this is important to you, you might
| consider a contract with the company that lays out cases and
| conditions where breakage is acceptable.
| marcosdumay wrote:
| Or you internalize the functionality with a library.
| TeMPOraL wrote:
| Yes, SLA is a way to turn literal _existence_ of a product
| into a pay-as-you-go offer on top of a service.
| JohnFen wrote:
| Yes. And if an updated library breaks you, you can fix it
| easily by rolling back to the earlier version of the library.
| If an outside service breaks you, you're hosed until they fix
| the service or you rework your code to route around the
| breakage.
| mooreds wrote:
| Previous discussion:
| https://news.ycombinator.com/item?id=26398960
| ChrisMarshallNY wrote:
| I thought it sounded familiar.
|
| Thanks!
| mooreds wrote:
| HN doesn't repeat, but it sure does rhyme. :)
| _ZeD_ wrote:
| (so that I can use your library and not depend on your service)
| cosmolev wrote:
| It was like this 20 years ago. "software that can be run by the
| user" - good old times.
| closeparen wrote:
| Services usually depend on databases. Libraries usually don't.
| Either you need to support every storage backend your users might
| have, require them to write an integration layer from your
| generic hooks, or expect them to provision and manage new storage
| when using your library. In any case you are asking them to do a
| lot more work (manage the data) and in some sense breaking
| encapsulation by making them responsible for this.
| z33k wrote:
| Great point. In my opinion it is possible and maybe even ideal
| to do both: make it easy for anyone to run their own service
| while also running your own service so that users have the
| option to not have to manage the data, patching and ops side.
| bee_rider wrote:
| The underlying point of the post seems to be that it is better
| to ask the user to do more work, than the developer.
| Kinrany wrote:
| This shows that we lack good abstractions over storage.
| ben_jones wrote:
| It's more like data storage needs are not one size fits all
| so it's better left to the user who best knows their storage
| needs.
| miningape wrote:
| This is interesting, makes me wonder if a "dockerised"
| database is something people could use. I mean a database
| frontend with its own language/protocols/whatever that
| allows you to define the data structure but leaves the
| specific storage engine or format as a backend detail that
| can change from platform to platform.
| mjr00 wrote:
| > I mean a database frontend with its own
| language/protocols/whatever that allows you to define the
| data structure but leaves the specific storage engine or
| format as a backend detail that can change from platform
| to platform.
|
| That's more or less a description of SQL.
| rekwah wrote:
| Postgres wire format is indirectly getting there. Plenty
| of tools use that with wildly different storage engines
| on the other end.
|
| A clean room implementation would likely yield different
| results but there appears to be some appetite for a
| solution.
| crabbone wrote:
| Nah. It's not that. We lack a concept that can organize
| storage. Let me illustrate this.
|
| So, until some years ago there was complete nonsense and
| anarchy in Linux networking management. That is until we
| got the "ip" program. There's still nonsense and anarchy,
| because the "ip" program doesn't cover everything, but it's
| on the right track to organize everything Linux knows about
| networking under one roof. So, vendors today, like, say,
| Melanox (i.e. NVidia) choose to interface with "ip" and
| work with that stack rather than invent their own
| interfaces.
|
| When it's extendable in predictable and convenient ways,
| user will extend and enrich functionality.
|
| Now, compare this to Linux storage... I want to scream and
| kill somebody every time I have to deal with any aspect of
| it because of how poorly mismanaged it is. There's no
| uniformity, plenty of standards where at most one is
| necessary, duplication upon duplication, layers... well,
| forget layers. Like, say, you wanted a RAID0, well, you
| have MD RAIDs, you have LVM RAIDs, you have ZFS RAIDs, you
| have multipassing with DM (is that a RAID, well sorta'
| depends on what you expected...) also, well, Ceph RDB are
| also kind of like RAIDs, DRBD can also sort of be like a
| RAID...
|
| Do you maybe also want snapshots? How about encryption? --
| Every solution will end up so particularly tailored to the
| needs of your organization that even an experienced admin
| in this very area your org is specializing will have no
| clue what's going on with your storage.
|
| Needs can be studied, understood, catalogued, rolled into
| some sort of a hierarchy or some other structure amenable
| to management. We haven't solved this problem. But we have
| an even bigger one: no coordination and no desire to
| coordinate even within Linux core components, forget third-
| party vendors.
| yjftsjthsd-h wrote:
| The space is complex enough that I wonder if it's _possible_
| to make abstractions that aren 't horribly leaky.
| pphysch wrote:
| I blame the SQL "standard". It's a massive, unnecessary
| abstraction layer that only complicates attempts to build
| bridges between code and relational databases (which I
| believe is the most general-purpose paradigm).
|
| Personally, I am working on a modern Python ORM for
| PostgreSQL and PostgreSQL alone.
| osigurdson wrote:
| You cannot abstract away a 3 order of magnitude difference in
| bandwidth and latency.
| pclmulqdq wrote:
| Where did you get a 3 order of magnitude difference? Are
| you still using hard drives for your storage medium?
| jandrese wrote:
| Isn't this what SQL is supposed to be? You bring the DBI for
| your database and plug it into the app. Shame that it doesn't
| work out so well in practice.
| Too wrote:
| Cloud providers are (counter to intuition about their lock-in
| incentives) improving the space of this. Lots of tools now
| allow configuring storage by just pointing to various cloud
| stores, most often a S3 compatible api, not exclusively
| though.
|
| K8s PersistentVolume is another decent shot at storage
| abstraction, only a bit raw.
|
| Finally, more and more tools expect you to have a Postgres
| they can plug into as backend.
|
| All above assumes you want to treat the library data as a big
| unknown blob. Once data start being corrupted and need
| bespoke repair, things are less fun. Access and retention is
| another fun rabbit hole.
|
| Data is complicated.
| jbverschoor wrote:
| sqlite is a library
|
| zeromq is a library
|
| That's all the storage you need
| bilalq wrote:
| No? If you have a horizontally scaled architecture or
| anything with multiple nodes, you can't just get away with
| "sqlite is a library".
| TeMPOraL wrote:
| SQLite is more about letting you get away with _not having
| a horizontally scaled architecture or anything with
| multiple nodes_ in the first place.
| ImPleadThe5th wrote:
| I'm maybe naive, but is it not possible to supply a repository
| interface for the user to implement? Bring your own glue?
|
| The library uses only the interface to work with whatever
| orm/db connector exists in the client project.
|
| If services at any given company all use a standard db library,
| it could even directly interface assuming your using that. I
| don't think we're talking about public apis and packages here.
| __MatrixMan__ wrote:
| It's only an unreasonable amount of work if you assume that the
| user is managing a separate storage backend for each library.
| If you take the Tim Berners-Lee approach (re:
| https://solidproject.org/) then each user is only managing one
| storage backend: the one that stores their data. The marginal
| cost of hooking in one more library to the existing backend is
| low.
|
| We just have to get a little more fed up with all of these
| services and then the initial cost of setting it up in the
| first place will be worth it. Any day now...
| closeparen wrote:
| I think most interesting web services are providing
| structured access to the same data for multiple people. A
| private, individual data silo wouldn't get the job done
| unless combined with some kind of message-passing. A silo to
| which users can invite peers is interesting, but it's an
| important characteristic of many web services that the
| specific read and write transactions allowed are application-
| defined... you don't actually want to give your collaborators
| general read or write access at the storage level.
|
| For example, it's important that I can add this comment, and
| I can't delete your comment, but the moderators can. The
| "storage" software would have to know something about the
| business logic of a web forum to make that happen.
| __MatrixMan__ wrote:
| I think we just need smarter browsers which can be
| configured to know who we trust in which dimension.
|
| If I want to leave a comment on an article and then delete
| it, I can publish the comment in my pod, and I can also
| publish the deletion. If you've got your browser in a mode
| where it's interested in my comments, it can pull in the
| data from my pod and render it in context with the article
| --whether or not the article's author cared to provide a
| comments section.
|
| If you drop the idea that anyone is authoritative about how
| it all comes together on the viewer's screen, you can also
| dispense with the headaches of being that authority (e.g.
| services).
| crabbone wrote:
| The title of the article literally says "where possible". You
| found a case when it's not possible, and decided to argue
| against that...
|
| No, not all services come connected with a database.
| Alternatively, often times a database is an artifact of tenancy
| and the need to manage users which would not be needed, had the
| functionality be exposed as a library.
|
| More importantly, whether users realize this or not, a library
| is more beneficial for them than a service in majority of
| cases. Much in the same way how it's almost always better to
| own something than to rent it.
|
| Just to give some examples of the above: all the Internet of
| crap stuff, all sorts of "smart" home nonsense which requires
| that you subscribe to a service, install an app on your phone
| and send all your private data unsupervised to some shady Joe
| Shmo who you know nothing about. To be more specific, take
| something like Google Nest thermostat. There's no reason this
| contraption should ever go on the Internet, nor should it
| require to know your street address, nor your email etc. In
| fact, the utility it brings is very marginal (saves you few
| steps you'd have to make to reach for the boiler's controls to
| program it). It absolutely could've been designed in such a way
| that it doesn't connect to the Internet, or, at least, to never
| leave the local area network, and yet it's a cloud service...
| hbn wrote:
| Is this really a common scenario where there's a choice between
| these 2 options that isn't obvious? I've never considered
| libraries and services to be two equal options of distributing
| functionality, and you just pick one of them. It's usually a
| function of practicality and monetization.
| marcosdumay wrote:
| > Is this really a common scenario where there's a choice
| between these 2 options that isn't obvious?
|
| In my experience, no, it isn't any common. But it is somewhat
| common for people to ignore the obvious option and go for
| services anyway.
| crazygringo wrote:
| Exactly. I've never run into a situation where there was even a
| choice.
|
| Is it something that relies on a private database, queue,
| massive processing, dedicated hardware, shared state, something
| geographically distributed? It's a service out of necessity.
|
| Or is it just a bundle of quickly executing code? Then it's
| obviously a library.
|
| I've never seen anybody try to turn leftPad() into a service.
| sokoloff wrote:
| Fair to assume it's a joke, but: http://left-pad.io/
| citrin_ru wrote:
| In my experience a new micro-service considered the default
| option for anything which could be done as a micro-service
| nowadays. Library API design is the lost art (almost). And the
| choose is not always obvious for non-technical reasons.
| Consider a following example: you have 3 micro-services X, Y, Z
| which need to interact with a platform G but this interaction
| requires non-trivial chunk of code. It can be done as a library
| or as a new adapter micro-service A which will encapsulate
| knowledge about platform G and will interact with X, Y, Z the
| way it would be easier for X, Y, Z developers to integrate.
| Micro-service will add network latency but will allow to
| make/deploy all changes only to service A. With a library one
| would have to test and release a new version and then ask X, Y,
| Z maintainers to switch to this new version. In some
| organizations it will be a very slow process because X, Y, Z
| can put an update request (from the library team) at the bottom
| of the backlog. With a micro-service A teams X, Y, Z would have
| much less power to stop/slow development.
| geraldwhen wrote:
| This is making technology objectively worse to solve people
| problems, and this only expands.
|
| More software needs iron clad leadership and control. Any
| organization that lacks this can't help but produce shit
| software. There has to be a single person with real decision
| making power than can force upgrades and prioritization of
| work, and they need to be able to axe people or teams who
| can't hack it.
| fmbb wrote:
| OK but most business compete in a market. And the people
| they employ are in a labour market.
|
| It seems that worse is better.
|
| What we see is just another manifestation of the current
| economic paradigm. Waste is winning.
|
| Making too many pairs of jeans so we burn tons of perfectly
| good new pairs daily wins.
|
| Throwing hundreds of engineers on problems bashing out
| hundreds of thousands of lines of code sending megabyte
| messages between dozens of service instances in multiple
| kubernetes clusters appear to be a winning move. Otherwise
| someone would beat them, right?
| geraldwhen wrote:
| Most engineering orgs don't understand or feel "the
| market" until mass layoffs. They exit their companies
| with beautiful resumes touting micro services and k8s.
| bardsore wrote:
| Worked a place where we switched from having a client library
| to making the other teams use our APIs directly themselves.
| Of course, we had to do the switch for them because they were
| too busy... Pretty much just copy-pasted the library code
| directly into their projects.
| mjr00 wrote:
| If you're distributing something publicly, it's fairly obvious
| which to pick, yeah.
|
| It's less obvious for internal systems and architecture. For
| example, your company wants to add domain-specific auditing to
| all of your existing services. You could have every service add
| a library dependency that lets them just call
| `auditor.log(...)` and the library internally writes to
| storage. Or you could add an auditing service with a full
| HTTP/GRPC API. Or you could go halfway and build an auditing
| service but provide a library that acts as an interface.
|
| There's no right answer for this IMO, all those approaches have
| pros and cons.
| bilalq wrote:
| This was a really common dilemma at Amazon. The prevailing
| wisdom was opposite to the advice in the article though. Unless
| you had an exceptional reason, your functionality and data
| should be exposed as service, not a library.
| manicennui wrote:
| Based on the fact that everyone seems to turn every desktop app
| into a service, yes?
| crabbone wrote:
| Oh, absolutely. Service brings convenience to the backoffice
| and money! Libraries suck to support and hard to sell.
|
| Of course users want libraries but vendors want services. There
| are plenty of examples where something could've been a library,
| if the vendor had user's interest at heart, but instead it's
| sold (or rather rented out) as a service.
|
| Go to Amazon marketplace, for example. Virtually everything
| there is a product that should've been a library but is sold as
| a service...
| sameg14 wrote:
| Yeah but then who is responsible for maintaining the library? If
| you have a bunch of internal teams that depend on your library
| and there is an issue or a feature request, you're back to the
| same position of being the one that does the work to implement.
| Better to have a service IMO, you can get telemetry out of it and
| scale it out or replace internals without having to worry about
| who will get affected.
| sidlls wrote:
| Many engineering organizations have "platform teams"--teams
| that have as their sole responsibility maintaining shared
| libraries and "core" services for the entire platform.
| marcosdumay wrote:
| As opposed to being responsible for maintaining and doing
| operational support of the service...
|
| I still don't get what advantage exactly you are expecting.
|
| Scaling it is only ever a problem for the service; you don't
| even have to think about it in a library. You can get telemetry
| from a library just as well as from a service; your users may
| find that a bit invasive, but it's still way less invasive than
| calling your service. And you can replace the internals of
| whatever, without having to worry about who will get affected,
| that's what defines internals.
| hamandcheese wrote:
| If you can maintain a stable service API, why can't you
| maintain a stable library API?
|
| I don't see any inherent reason why it should be easier to
| change the behavior of a service rather than a library.
| TeMPOraL wrote:
| Because if you ship code to users (library), you lose
| control. You may want to change something, but your users
| will just tell you to go pound sand. Conversely, if you keep
| the implementation on your side (service), you get to control
| how things work and when things change, and your users don't
| have a say in this.
|
| It's an ownership and control issue.
| bigstrat2003 wrote:
| I don't see the issue here. If I release my_fancy_lib
| 2.0.0, and I have users who only ever want to stay on the
| last 1.x release, that's fine. It's no skin off my nose if
| users choose to stay on an old version forever.
| TeMPOraL wrote:
| Yeah, exactly. However, if you were to make it a service,
| those users would _have to_ switch to 2.0.0 or stop using
| it, as 1.x no longer exists.
| taneq wrote:
| Write libraries instead of services. Where necessary, wrap
| libraries in services.
| buzzy_hacker wrote:
| Related: Who Does That Server Really Serve?
|
| https://www.gnu.org/philosophy/who-does-that-server-really-s...
| psd1 wrote:
| Stallman performs the vital function of pinning the Overton
| Window to the left, which benefits those of us with more
| nuanced views.
|
| I'm not going to run my own mail server in 2023, etc.
| teddyh wrote:
| > _I 'm not going to run my own mail server in 2023, etc._
|
| But many do. It's still not that hard.
| slifin wrote:
| I hope GraalVM's Polygot runtime will eventually allow more
| companies to go with libraries over services in more situations
| for multi-language businesses.
|
| And Polylith for better reuse across projects generally.
| pas wrote:
| > Polylith
|
| this sounds very nice on paper (it sounds common sense trivial
| utopia that everyone sets out to do anyway, but somehow life,
| entropy and deadlines get in the way), but ... is there a
| bigger real life project using something like this?
| cornfeedhobo wrote:
| I wish I could nuke GraalVM from the earth. It ruined my life
| for 2 years and I will never forgive it. It's a very stupid
| idea, pushed by CTOs that think Java's "write once, run
| everywhere" is still relevant. It literally runs every language
| slower and introduces all kinds of build pains.
|
| Ugh, I can't believe I had to read your comment this morning.
| smh
| eurekin wrote:
| I was stumped the first time I read about Graal. It felt like
| 15 years too late and made zero sense to me.
|
| Please, if you could share more about that, I'd be happy to
| share the pain
| NBJack wrote:
| It's all fun and games until you hit version n+1 or n+2, and
| often realize how slow many customers are to upgrade the library.
| Then there are the potential conflicts in your own dependencies.
| And let's not forget the occasional breaking change you
| introduced.
|
| You are now sacrificing the money saved by not hosting to
| maintain what will likely be a growing matrix of possible
| versions, underlying assumptions, and my personal favorite: weird
| customer deployment scenarios that break your library
| functionality. Bonus points if you are developing in a language
| without strong typing, or need to integrate with a dependency
| manager.
|
| Plus, if there was anything about your service that leveraged
| unique algorithmic improvements or some other proprietary tech,
| it is now at the mercy of anyone with a decompiler and sufficient
| time.
|
| I suspect the author may change their mind if exposed to scaled
| up solutions and technologies.
| mooreds wrote:
| I thought the author addressed that point:
|
| "But this assumes that slow-to-upgrade users can have negative
| effects on everyone else. If one user can't have a negative
| impact on other users, then you don't care if some users are
| slow to upgrade; they're only hurting themselves."
|
| There's still the support issue, I agree. If a customer paid
| you money and they are on version n-10, they still expect
| support.
|
| > Plus, if there was anything about your service that leveraged
| unique algorithmic improvements or some other proprietary tech,
| it is now at the mercy of anyone with a decompiler and
| sufficient time.
|
| This is a valid point. My answer would be: it's all tradeoffs,
| but if your secret sauce is so valuable that it would be worth
| decompiling and can't be protected with decent pricing, license
| terms and lawyers, run a service.
|
| There are plenty of technologies that are not worth decompiling
| for your average business customers.
| jampekka wrote:
| In my experience the more they try to hide the sauce the
| worse it is.
|
| Quite logical though if the secret is that your secret sauce
| is bland and off-flavor copy of a canned soup. Which it
| usually is.
| LtWorf wrote:
| If your updates don't break every single time, your customers
| will be more likely to do them.
| gjadi wrote:
| > It's all fun and games until you hit version n+1 or n+2, and
| often realize how slow many customers are to upgrade the
| library.
|
| How is that different with services? I don't develop services,
| but I can imagine that before breaking the services, you have
| to poll with your biggest customers and you don't break until
| they are ready to move to the new version. The alternative I
| can imagine is to keep providing the old services with a grace
| period (e.g. /v1 will be available until dec next year).
|
| What am I missing?
| sokoloff wrote:
| Many changes are believed to be non-breaking and so you can
| be running only one version in prod. (Most of the changes
| _believed to be_ non-breaking are non-breaking.)
|
| With a library, you end up with many different minor or point
| versions running without control over it.
| gjadi wrote:
| Ok so the benefits here are transparent upgrade. Thanks.
| genman wrote:
| Emphasis on "are believed". This actually puts clients into
| risk of having breaking changes out of their control.
| materielle wrote:
| The idea is to link a thin client library into the user's
| code.
|
| Then the thick client is controlled server side by the devs.
|
| If you design the thin and thick client intelligently, you
| can make a lot of changes to the system by merely modifying
| the thick client (which you control), without needing to
| update the thin client.
|
| Problems tends to come in a few flavors, namely that this
| increases complexity and that _some_ changes will always
| require modifying the thin client.
|
| The biggest pitfall, though, is that you have to actually put
| thought into the thin client design. "Thin" is an emergent
| property of a well thought out client. You can't just "touch
| thin_client.java" and think because you named it "thin" that
| it's inherently decoupled from the service. You have to
| actually put thought into it.
|
| Your example is exactly right. The service can support either
| v1 or v2 depending on the version of the thin client.
|
| The main other technique is to have the thin client
| communicate in a relatively abstract and generic manner. For
| instead a "write()" endpoint that accepts an "options"
| dictionary.
|
| "write()" is so generic that it probably won't have to
| change, and changes in behavior can be modified by shoving
| stuff into dict, perhaps with a "version" field to instruct
| the server how to interpret the call.
|
| When you add a new feature, you can bump the thin client to
| v2 and shove new options in the dict. Then the server can
| support v1 and v2. But also, you can modify the server to
| handle v1 differently. E.g the absence of a
| "disable_new_feature" key automatically opts v1 callers into
| the new feature.
|
| I think the details are are really coupled into what you are
| engineering and what you want to roll out. There's no magic
| bullet, and this increases the complexity of your code. Good
| engineering in my opinion is deciding when these approaches
| are worth it and _how_ they should be implemented.
| gjadi wrote:
| I don't quite understand what you are saying.
|
| As per the article, the advantage of a library over a
| service is that you don't have the burden of maintaining
| the said service. How having a thin client and a server
| helps?
|
| I also don't see how "shoving new options in the dict" can
| help. From my POV, if the client needs to be updated to
| benefit from the new features, then there is no way around
| it, work has to be done. From there, I much prefer having
| sensible name, parameters, etc. from a library that can be
| leveraged by static typing rather than a documentation for
| a JSON API (I know API-as-specs exist but I've never used,
| maybe that's why I think that).
| zja wrote:
| You have the same problem with services and libraries when
| you're introducing a breaking change. With services you can
| make non-breaking changes like security patches on the server
| side, without needing to coordinate with a customer.
| LtWorf wrote:
| And with libraries you can't make breaking changes?
|
| The fact that I don't need to recompile everything whenever
| libcurl or libssl has a security fix proves otherwise.
| grogenaut wrote:
| Simple answer is YOU control the deploy of a server in
| miroservices, so YOU decide when it goes live. You aren't
| playing spreadsheet telephone at scale to get everyone even
| im an internal org to update. If you're smart you have your
| callers send version information and you usually just add
| fields, only removals or renames cause version issues. If
| you're having everyone use a library they control when they
| update the logic and you have less telemetry when some wierd
| corner of your company is using out of date logic.
| noah91734 wrote:
| > It's all fun and games until you hit version n+1 or n+2, and
| often realize how slow many customers are to upgrade the
| library.
|
| Given how frequently needless breaking changes are made, or
| features are removed and paywalled, I would consider this a
| feature rather than a bug. Sometimes I have higher priorities
| than working to support someone else's breaking changes.
|
| Two years ago, I was using an official library for interfacing
| with a video chat service, and they decided to break the
| underlying API without updating their library, so I had to
| rewrite the library myself.
| kuchenbecker wrote:
| Write a library, deploy it as a service if necessary.
| crabbone wrote:
| This is a completely broken model.
|
| You are wrong by pushing your changes on "slow to update"
| customers. Customers know when to update much better than you
| do. If you are trying to update before they want to, you are
| doing them a disservice.
|
| This attitude is inspired by the desire to sell more, and often
| times, it means to sell more unwanted crap, where customers are
| trapped by the "package deal", where they are either forced to
| update to gain useless features and the headache resulted from
| various inconsistencies and defects coming from the last
| update, or they are unable to get the product at all (because
| the provider cancels support or wouldn't sell older versions
| etc.)
|
| In other words, you with straight face describe some really
| shady practice and you don't even realize how bad it sounds.
| actionfromafar wrote:
| It can be that, but it doesn't have to be. It depends on the
| business model and it's quite orthogonal to library vs
| service.
| fsloth wrote:
| Yes you need to have n versions you support in parallel in the
| wild. One per released incompatible binary API (so in semver
| 1.x, 2.x etc). We support about 30 libraries with 5 ppl spread
| around a fortune 1000 org here and there. It's quite tenable,
| we are just super strict regarding ticket policy and release
| notes, so we always know what's released where. Just have a
| system. Stick to it. Keep things organized. Works like a charm.
| If someone wants a service out of those someone just packages
| them to docker and puts them to backemd somewhere. We can focus
| on pure business logic, someone else maintains services etc.
| Really nice setup.
| wqtz wrote:
| I don't get it. Can anyone kindly share where this practice can
| be applied to a commercial software? I am struggling to wrap my
| head around "how run by user" works where they are using a
| commercial software service.
| Sanzig wrote:
| License fees, just like the old days. Either fixed cost to
| license the library, a royalty based scheme (# of
| users/installs), or both.
| denton-scratch wrote:
| There's some confusion in this thread between "service" as out-
| of-process functionality called via IPC or over the network,
| and "service" as something done by a service-provider for
| money, such as emptying your bins.
| catern wrote:
| Ultimately, it can't. Proprietary software has fundamental
| limitations that force proprietary software developers to
| choose technically inferior designs. It's why in the long run
| proprietary software is doomed.
| throwaw12 wrote:
| "where possible" is a keyword here and easy to argue about. For
| example: * my logic requires a database *
| my logic should be isolated for compliance reasons * my
| logic accesses a service which is not available for everyone
| * my logic should be processed async and needs to store state,
| how do I make sure library owners have that environment *
| customers embedding my library are not upgrading it frequently,
| which leaves us to support 15 years of libraries * ...
| leethomas wrote:
| Hm, it feels like all of these except for number 2 and the last
| one can be solved by appropriate interfaces and documentation.
| mooreds wrote:
| On that topic, I love this article from Stripe (in 2017)
| about how they version their APIs:
| https://stripe.com/blog/api-versioning
| throwaw12 wrote:
| how about 1?
|
| Imagine a scenario, your service is a low traffic, but
| service which embeds you as a library is a high traffic with
| many instances and always opens DB connection.
|
| Why should you optimize your Database for high traffic use
| case, when your use case is really a low traffic?
|
| And then repeat this for 10 other libraries and library
| owners. Everyone is optimizing for nothing.
|
| make it even more difficult, 100 different types of services
| with different traffic patterns are embedding your library
| with different behaviours when it comes to managing DB
| connection state
| throwaw12 wrote:
| how about 3?
|
| Scenario: your service is accessing a service which exposes
| PII data and you only process them.
|
| Service which embeds your service enabled audit logs of
| network requests and made it visible to everyone in the
| company. You have created a risk unintentionally
| whalesalad wrote:
| It's hard when you have N services that need a lockstep migration
| from v1 to v2 of the library. I tend to agree with much of this
| but it's not a one size fits all thing.
| jampekka wrote:
| And write unix-style cli tools that can be piped to and from.
| Make them eat and spit something structured like JSON.
| JoshTriplett wrote:
| Not as a primary interface. By all means provide a wrapper
| around the library that does that, for users to use on the
| command line, but there should be an underlying library for
| other programs to use.
| jjtheblunt wrote:
| Isn't powershell a generalized version of that idea?
| jampekka wrote:
| It's a specialized version of that idea.
|
| Problem with libraries are that they tie the user to a
| specific language/OS. A cli tool eating and spitting
| ASCII/UTF-8 is about as cross platform as it gets.
| throw555chip wrote:
| No actually, it tries to be a kitchen sink and is the
| opposite of the UNIX concept of pipelines with small purpose
| built utilities.
| romankolpak wrote:
| i get the benefit of offloading the admin costs onto your
| consumers, but, as always, the devil is in the details. i've met
| both cases where a library should've been a service, and vice
| versa. this advice is way too broad and abstract to be practical
| revskill wrote:
| Why not both ?
| 3cats-in-a-coat wrote:
| Those aren't "either or" things. Usually services are exposed
| through libraries. And a library requires a service if it has a
| canonical or centralized storage or processing, which can't be
| done locally.
|
| Now, sure, we do stupid things as services, for sure. But people
| often do it to monetize the service, or control the users. And so
| that'll never change.
|
| I recall someone making an "async HTML5 AJAX blink service" as a
| joke for this trend.
| osigurdson wrote:
| If this isn't obvious, we are lost. Creating a rest endpoint
| where a library would do is pure insanity.
| Micoloth wrote:
| And yet..
|
| I wholeheartedly agree with your sentiment.
|
| Unfortunately, the obvious economic incentive to effectively
| paywalling code through the Internet, is so strong that _on
| this very website_ (that was supposed to have hacker mentality)
| most of the comments are in favour of it, because "you have to
| make a living"...
|
| Yes, I think we are lost.
| fragmede wrote:
| If you have any feasible ideas that could plausibly work
| where people don't "have to make a living" so that we can
| find ourselves and not be lost, we're all ears. Until then
| we're stuck in a place where people need money to pay for
| frivolous things like "food" or "rent" or "transportation".
| Still, in the face of that, there's a site called GitHub
| where people do freely share code, despite the economic
| misincentives, so I think the kids will be alright.
| NohatCoder wrote:
| Of course the prevalent reason to make a service is that it can
| be monetized to Hell and back again. Does it fit the customer's
| needs? Who cares, as long as they have no clue what they will end
| up paying until it is too late to migrate away.
| AgentOrange1234 wrote:
| Meh. Yes that happens and yes it can be frustrating. But...
|
| If users want something with long-lasting support and
| enhancements, that's going to take work. The people who do this
| work are going to need to earn a living.
|
| There are lots of ways to accomplish this. Advertising,
| altruism, one time purchases. But charging for a service can be
| a perfectly reasonable way of making it sustainable.
| lovasoa wrote:
| I agree with the reasoning. However, an important point to note
| is that libraries are much harder to monetize than services.
| jagged-chisel wrote:
| You write a library, then wrap a thin service interface around
| it. Distribute the lib as needed. Publish the service as needed.
|
| Maintain the library, modifying the service on as it is affected.
|
| So this effectively comes down to "write a library" as tfa
| suggests. But there's no reason the library can't then be the
| core of a service.
| soulofmischief wrote:
| I take "where possible" to mean that what you describe should
| be considered an exception instead of a rule, which I agree
| with. I do have some thin services which are libraries, but
| above and beyond directly importing libraries is preferred.
| With edge functions becoming more popular, this also seems to
| be the preferred pattern, having "fat" edge functions with
| shared code, vs many small edge functions calling each other.
| mooreds wrote:
| > You write a library, then wrap a thin service interface
| around it. Distribute the lib as needed. Publish the service as
| needed.
|
| At $CURJOB, we did this but at a higher level of abstraction
| (an authentication architectural component, rather than a
| library). I think this is what the author means when they say
| "writing a standalone server reached through a network
| protocol".
|
| We see a lot of folks who like the flexibility of consuming
| functionality as a service or library, as they see fit. We've
| even had customers who said "we chose you because now we want
| you as a service, but later will want you as library" or vice
| versa.
|
| Flexibility isn't free, though. Versioning, support, backwards
| compatibility (features and performance), even offering the
| service all become more complex.
| inetknght wrote:
| I've been writing services... as libraries first. Then just wrap
| the library in a very simple `main()`:
|
| ``` #include "servicelib.hpp" int main(int argc, char argv) {
| return servicelib{argc, argv}.run(); } ```
|
| The library can be re-used in other apps or services.
|
| Then the whole damn library is unit-testable with any arguments
| you throw at it. Got an OS where argv may be null? You can unit
| test that. Got a user who decided to use --iamstupid instead of
| --iamawesome? You can unit test that too. Want to set up
| environment variables? Well that's not thread safe, but your test
| harness can do it before it instantiates the library object.
|
| Want to use semver? You can. I use git commit checksums for
| versions and automatic tagging to semver. It's more annoying but
| superawesome.
| agomez314 wrote:
| Can you give details on how you do "automatic tagging"?
| jampekka wrote:
| Hundred times this. Nice to hear an odd sound of sanity amisdt
| the architecture astronaut crowd. YAGNI.
| Joel_Mckay wrote:
| We usually wrap a set of valgrind debugged small test/demo
| programs that hammer a library to monitor for leaks etc.
|
| However, ensuring thread safety can sometimes be a challenge.
| =)
| giancarlostoro wrote:
| Used to do this in .NET for personal projects then got pulled
| into various dotnet projects that had their own approaches. But
| it was really nice. What I had hoped to eventually achieve is
| being able to hotswap the library somehow so I can have a
| server with zero downtime for updates, but never found the time
| to do so. I would assume I would do it via a microservice type
| of architecture instead.
| capableweb wrote:
| You can just do this with nginx or whatever you have in front
| (IIS if you're into Microsoft stuff I guess?). Run service at
| port xxx1, this is your "live" port. When you wanna upgrade,
| launch service to port xxx2, do graceful reload of config,
| swap the ports around so "live" port now points to right
| service, graceful reload of config again and done!
| giancarlostoro wrote:
| I should have added I was not doing a web service.
| swagmoney1606 wrote:
| IIS makes me want to explode and then die
| neonsunset wrote:
| In .NET for plugin management there are unloadable assemblies
| so you can dynamically load and unload e.g. plugins at
| runtime. However, it requires care, has caveats and the
| industry generally gravitates to other techniques for zero-
| downtime and/or rolling deployments where the replicas
| (nodes) are at first drained, then shut down, and replaced
| one by one or in groups but never in a way where there are
| none to serve the requests.
| flatline wrote:
| Containers can run services without most of the overhead they
| talk about. And as everyone else is mentioning here, a service-
| oriented architecture is not dependent on any one tech stack,
| it's just a way of designing applications.
| prpl wrote:
| I think this is roughly true, but at a BigCo it's not really
| feasible/easy unless you have a monorepo or otherwise extremely
| good build/integration tooling to deal with many repos (though Go
| can sort of deal with this)
|
| The issue is coordinating changes (and, god help you, library
| releases) across repos is often an utter nightmare with multiple
| PR/merge builds
| scubbo wrote:
| > it's not really feasible/easy unless you have a monorepo or
| otherwise extremely good build/integration tooling to deal with
| many repos[...] The issue is coordinating changes [...] across
| repos is often an utter nightmare with multiple PR/merge builds
|
| I'm coming up on a year out of $BIG_TECH_JOB (where the idea of
| a monorepo was horrifying) and transferred to
| $WAY_SMALLER_NON_TECH_COMPANY_WHO_USES_TECH_JOB (where the
| enthusiastically use a monorepo), and have been really confused
| by repeated claims like this. I really feel like I'm missing
| something here, because lots of obviously-smart-and-experienced
| folks repeat it. I'd really appreciate it if you could check my
| understanding and see what I'm missing.
|
| (To be clear, here I'm assuming that a monorepo is a single
| repository which contains conceptually-distinct-but-related
| projects - things which _could_ justifiably be their own repos,
| but which are kept in one repo for reasons of maintenance and
| managements - and wherein the build system is such that every
| sub-project within the repo uses the same package dependency
| tree, i.e. if ProjectA and ProjectB in the monorepo depend on
| LibraryZ, then the versions of Z that A and B depend on must be
| identical for any given commit/build. If I've misunderstood
| that - if that's just straight-up not what a monorepo is, or if
| it _is_ but with some extra nuance or sauce - then I guess we
| can short-circuit the response pretty quickly :P )
|
| Here's how a release of a breaking change of a library would
| work in a polyrepo world:
|
| * I publish v2.0.0 of my library
|
| * Consumers of that library are notified that a new version
| exists (via automated email notification, Dependabot, whatever)
| \ * Anyone who _wants_ to update can do so (independently and
| at their own rate) - anyone who's comfortable staying on the
| old version can do so
|
| ** If we really want to make things easy for consumers, we can
| make automated PRs against their repos (this would be the
| "extremely good build/integration tooling to deal with many
| repos" you refer to, I suspect? Something like Spotify's
| FleetShift[0]) to make the change - though in practice this is
| probably way more trouble than its worth, I've only seen it
| done for serious security vulnerabilities where a) everyone in
| the whole damn company has to b) upgrade RIGHT THE FUCK NOW.
|
| * Time goes by
|
| * v1.x gets deprecated
|
| * Anyone still using v1.x gets notified that they are using a
| deprecated version and strongly encouraged to update
|
| * A little more time goes by (not much!)
|
| * Consequences Occur for people still using the old version -
| this could be a visit from your friendly InfoSec enforcer, or
| automatically failing builds, or the removal of v1.x from the
| package repository (which will indirectly cause failing
| builds), or...
|
| Conversely, with a monorepo, the situation seems to be:
|
| * I publish v2.0.0 of my library
|
| * Every single team whose code is in the monorepo must
| coordinate to make the changes to consume version 2. This
| change, by definition, proceeds at the pace of the slowest team
| - if one of them is underwater on oncall, or has their only
| competent engineer on PTO, or has some quirk of implementation
| (or dependency on old feature) which means they can't update
| for two months, then _the whole dang monorepo_ is staying on
| version 1 for two months; no ifs, ands, or buts
|
| ** But, yes, if you want to a wide-ranging refactoring to
| change all the in-monorepo consumers to use the new method,
| then yeah, modern IDEs are going to have _some_ functionality
| built-in for that within a single repo. But - by virtue of
| being a breaking change, it's pretty likely that the change-at-
| call-site is going to be more complex than simply changing the
| type or name of the method being called. Maybe the returned
| object needs different methods called on it. Maybe the method
| requires an extra parameter (which refactoring IDEs can add to
| the actual callsite, but cannot implement for you _fetching and
| providing_ that parameter). At this point, either humans are
| going to have to comb through the changes to finalize them (at
| which point, you lose the claimed advantages of being in a
| monorepo where changes can be done by tooling), or you're going
| to have to implement some sort of code-parsing system to make
| correct changes throughout (which, again, is approximately
| equivalent to the automation work required in the polyrepo
| case)
|
| So: while, yes, it _would_ be a hassle to "coordinate changes
| across repos" (though _significantly_ less to coordinate across
| a single repo-to-repo boundary, especially if both are owned by
| the same team, than it would to coordinate a whole monorepo's
| worth of interactions), the joy of a polyrepo situation is that
| _you don't have to_. Consumers can update when they want to, at
| their own pace - no coordination necessary! So - yes, there
| will be a greater _volume_ of PRs required in a polyrepo
| situation, but a) each of them will be way simpler and the
| total volume of work will be , b) they are independent (so
| teams who don't want to update do not hold back those who do).
|
| But - people keep saying "it's easier to make wide-ranging
| changes in a monorepo", so I _must_ be missing something. What
| is it?
|
| [0] https://engineering.atspotify.com/2023/05/fleet-
| management-a...
| evntdrvn wrote:
| What I've heard from friends working at places where the
| monorepo model has worked well, is that it also involved a
| culture shift such that the burden of upgrading the consuming
| services is put on the shoulders of the _providers_ of the
| library, rather than the consumers. This implicitly brings
| along some benefits, like making the providers not cause
| excessive/unnecessary version churn and ensuring easy upgrade
| paths, because they experience the pain rather than it being
| externalized onto the consumers :) Also, good tooling helps--
| and I think that this model also encourages investing in
| that.
| scubbo wrote:
| Wow, that is certainly a _huge_ culture shift. It certainly
| explains why it's not sitting right with me - while I do
| think that "providers not caus[ing] excessive/unnecessary
| version churn and ensuring easy upgrade paths" is a good
| thing in isolation, it seems like it would be massively
| outweighed by the downsides of forcing a library provider
| to maintain familiarity with all their consumers' codebases
| and business areas. Seems to me like that would make it
| impractical to make any changes to a library that is
| consumed by more than a few other teams - which runs
| totally contrary to the intention of extracting _commonly_-
| depended-upon logic. The ideal situation would be to create
| a library that is _so_ popular that maintaining that level
| of familiarity is literally impossible.
|
| There are already incentives in place to ensure that a
| library provider is doing right by their consumers
| (building features that they want, making upgrades not too
| arduous), because if they make a hard-to-use library then
| people won't use it which should show up in however their
| team's success is judged. Seems to me that going the extra
| mile from "you have to provide a good desirable library" to
| "and you must also be familiar enough with your consumers'
| services to do the integration/upgrade work _for_ them"
| gives no new upside but all downside - service teams
| _already_ know their domain area, why should someone else
| have to?
|
| (I recognize that you're just reporting what you've heard,
| I'm not arguing _with you_, I'm trying to reason out the
| arguments in my head to get them straight)
|
| Really appreciate the insight, thank you!
| mpweiher wrote:
| Not just instead of a service.
|
| _Always_ write a library first.
|
| Not for any specific benefits, though these exist, but for
| architectural reasons. You can trivially wrap a library into
| pretty much everything else, but not the other way around.
|
| In fact, on macOS/iOS I put _all_ functionality in frameworks, as
| these have structure and can thus contain other frameworks and
| non-code resources, which is more difficult with a library.
|
| https://blog.metaobject.com/2020/06/mpwtest-only-tests-frame...
| denton-scratch wrote:
| Why does Windows seem to consist largely of a _huge_ number of
| services?
|
| Why do those services seem to have names that provide precious
| little guidance on what they are for?
| rascul wrote:
| > By library, I mean any software that can be run by the user:
| shared objects, modules, servers, command line utilities, and
| others. By service, I mean any software which the user can't run
| on their own; anything which depends (usually through an API) on
| a service provider for its functionality.
|
| These seem to be odd definitions and they make the article hard
| to reason about.
| catern wrote:
| Do you have better suggestions for what words/phrases to use to
| refer to these two categories?
| yawboakye wrote:
| > By library, I mean any software that can be run by the user:
| shared objects, modules, servers, command line utilities, and
| others. By service, I mean any software which the user can't run
| on their own; anything which depends (usually through an API) on
| a service provider for its functionality.
|
| this definition makes (open source) self-hosted services
| libraries too, and so i think it's wrong and unusable. the
| distinction between a library and a service is clear enough and
| colloquial at this point that a redefinition probably obscures
| rather than clarifies. a library isn't runnable (~has no binary),
| a service is runnable (~has a binary)
| catern wrote:
| What do you suggest as an alternative way to express these
| concepts?
|
| Colloquially, "library" and "service" have 95% of the correct
| connotations.
| klysm wrote:
| Even executables imo should be very thin wrappers around
| libraries
| nlawalker wrote:
| I have a hunch that a lot of people who have been spurred to
| think critically about this today are realizing that what they're
| actually interested in are services (in the economic sense) and
| not software.
| sidlls wrote:
| Libraries and services both have maintenance costs and upgrade
| impedance from clients. The costs might differ, but in my
| experience work out to about the same overall. The correct way to
| determine whether a piece of software should be a library or
| service is by examining its intended purpose and its
| dependencies.
|
| - If the software is dependent on another service or a data
| store, it should be a service: this provides the owners freedom
| to include error handling and observability that is appropriate
| for the service and provides protection for its dependencies
| against unbounded access (via observability at minimum, or human-
| organized contracts, etc.). Examples: software to retrieve user
| data from a database, software that aggregates data from a user
| service and an inventory service to produce a purchase history
|
| - If the software is self-contained, e.g., it does math or "pure
| business logic" algorithms, it _probably_ should be a library:
| performance can be optimized for one or a small handful of common
| use cases, error handling and observability become the
| responsibility of clients, and neither owners of the library or
| clients of it have to concern themselves with the impact to
| transitive dependencies (e.g., load added to a database).
| Examples: software that transforms user input into internal
| serialization formats; software that validates data, encrypts or
| decrypts data, or otherwise is "purely functional"
| layer8 wrote:
| It's not an either or. As argued elsethread, it should arguably
| always be a library (though not necessarily a published one),
| and optionally (if needed) also a service that wraps the
| library.
| mgaunard wrote:
| The difference lies in deployment.
|
| A library is a piece of code that you embed. A service is already
| deployed and configured, ready to use.
|
| Services provide a much nicer boundary for splitting
| responsibilities in any SaaS-like organization.
| Manouchehri wrote:
| Agreed. Building libraries from multiple private repos is hell
| in comparison to just having each as their own service.
| Joel_Mckay wrote:
| Indeed, because it is easy to keep updated with compatible
| library package standards (
| https://en.wikipedia.org/wiki/List_of_software_package_manag...
| ).
|
| Docker and Snap core use-case purpose is a necessary
| compatibility layer. =)
| eikenberry wrote:
| 90% disagree.
|
| At an enterprise scale shop the service model will always win as
| it will better match the organization structure and avoids the
| maintenance burden of having to keep a dozen or so libraries in
| sync. Enterprise shops never standardize solely on a single
| language as they are filled with exceptions and the new hotness.
|
| Everyplace else I'd say it will vary but I agree with the general
| consensus that shooting for the model of a library that can be
| easily run as a service is your best approach.
| throwbadubadu wrote:
| But you can add the generic all problem solving run a library
| as a service service wrapper around it and have both and be
| done forever?
| Matthias247 wrote:
| I had the same opinion in the past. But after some more years of
| work experience - mostly in the managed services area - I don't
| think it's that clear anymore:
|
| - If you offer your users a library, you are competing with tons
| of open source libraries which claim to offer the same thing. A
| lot of those will be incomplete, buggy or insecure. But most
| potential users will never know and try to get them work instead
| of looking at your offering.
|
| - If you are offering a library, debugging and user support can
| at times be challenging. Do you expect user to look at the
| internals (source code) of your library? Provide core dumps?
|
| - Having to support N different [major] versions of libraries can
| become challenging. It's hard to know when all users have
| upgraded. With a service, you can control the update schedule.
| Even though changes to public APIs of the service certainly are
| still problematic.
|
| - Write a library - but in which language? You might prefer Rust,
| but your users might prefer Go, Java or Python. You could write
| the core in one language, and add wrappers in other languages,
| but some users will still be unhappy with it (e.g. because it
| makes compilation difficult, people don't want "unsafe C code" in
| their high level language project, or the wrapper might slow down
| performance).
|
| - Libraries which are general-purpose and are not targetting a
| specific application/service can over time become feature bloated
| since things are added "just for the case that someone might need
| it". This makes them hard to maintain. And since there's no
| feedback/telemetry, it's also hard to say whether something can
| be safely removed.
|
| Note that all of this doesn't mean "don't write libraries". Even
| if you write applications/services, its good to structure
| internal components into libraries. It's mostly about "what is
| preferrable to offer for users".
___________________________________________________________________
(page generated 2023-11-23 23:01 UTC)