[HN Gopher] No Abstractions: our API design principle
___________________________________________________________________
No Abstractions: our API design principle
Author : jackflintermann
Score : 312 points
Date : 2024-04-25 19:15 UTC (1 days ago)
(HTM) web link (increase.com)
(TXT) w3m dump (increase.com)
| jackflintermann wrote:
| Author here - this has been a useful mindset for us internally
| but I'm curious if it resonates externally. I'd love your
| feedback!
| spandrew wrote:
| Love the article.
|
| If you love Stripe (and as a designer and tech entrepreneur I do
| - Stripe's simplicity and front-end skill is incredible) you
| might look at them and copy their ability to simplify and deliver
| polished experiences.
|
| But the real mastery of Stripe is that they know their customers
| -- and the simplicity they crave.
|
| By this article is sounds like Increase does as well and has
| forged a similar laser-focus on what their customers need to
| build terrific design guidelines for making products. Inspiring
| to see.
| rtpg wrote:
| Yeah I do think you can see in Stripes API places where there
| are differing tensions between "let's make this potentially
| universal" and "let's accept that this stuff is going to
| probably only apply for one payment method in one market".
|
| Personally I appreciate when the latter happens, but there's an
| aesthetic decision there
| esafak wrote:
| How Stripe builds APIs and Teams:
| https://www.youtube.com/watch?v=IEe-5VOv0Js
| chowells wrote:
| If there's no abstraction, what's your value-add? I don't care
| enough to read your marketing BS to see where you claim to be
| special, but... If your API is doing the exact same things as an
| underlying service is doing, you're just a middleman extracting
| rents.
|
| You might find it more valuable to state your position as
| "carefully scoped abstractions" to make it clear what value you
| add.
| koreth1 wrote:
| Based on my previous experience on payment systems, there's a
| surprising amount of value in not having to maintain direct
| business relationships with the underlying payment providers.
| It is much, much easier to work with a company like Stripe than
| to work directly with Visa and MasterCard and the ACH network,
| and heaven help you if you're a small company that needs to do
| automated cross-border payments to a wide range of countries
| without a middleman. You'll probably also get much better
| support from a tech-focused company when an API starts freaking
| out.
| exe34 wrote:
| I thought I understood everything you said, but isn't Stripe
| a middleperson here?
|
| > without a middleman.
| koreth1 wrote:
| Right, Stripe is a middleman and part of the value they're
| giving you is that you don't have to work directly with the
| underlying payment companies. If you had to support the
| same range of payment options without a middleman, you'd
| need to have business relationships with a bunch of payment
| companies, which would be a lot more difficult and time-
| consuming.
|
| Hope that's clearer!
| exe34 wrote:
| Makes sense thanks!
| OJFord wrote:
| Yes, GP's point is 'good luck to you doing that yourself,
| without a middleman [such as Stripe]'.
| jackflintermann wrote:
| Yes, exactly, the important thing to us is that our users
| don't need to build an additional mental model between us and
| the networks we sit atop. If you know the network, we want
| you to be able to intuit how our API works. There's a very
| real difference (arguably the fundamental value-add of our
| company) in the transport layer, though. The actual mechanics
| of integrating with, say, FedACH, are a bit long to get into
| here (we get into it a bit here if it's of interest:
| https://increase.com/documentation/fedach) but suffice to say
| it doesn't have a REST API.
| koreth1 wrote:
| That's an excellent point too. Some payment systems have
| abysmal technology. The product I worked on was focused on
| international payments and in a couple cases, the "API" was
| literally, "Upload a CSV file via FTP, and at some later
| point, another CSV file might appear on the FTP server with
| some of the payment results, but if it doesn't, call us
| because we probably just forgot to upload it."
| nijave wrote:
| Batch jobs and (S)FTP. In a bit of a weird twist, back
| when I worked at Chase, they were innovating on the
| ancient technology but it was things like "better batch
| job management/orchestration" and SFTP proxy to route
| between different servers and centralize key management
| jrochkind1 wrote:
| I'm curious to learn more about what your customers look
| like, what sorts of businesses and activities they are in.
| Where stripe's customers are working on products unrelated
| to payments, yours are working on products related to
| payments? I'm having trouble conceiving of examples.
| wodenokoto wrote:
| But it must have _some sort_ of API. Since your rest API is
| modelled on their API it made me really curious about how
| you communicate with those networks.
| fendy3002 wrote:
| Idk how this one works, but credit card processors need
| license. If you can use credit card service without requiring
| that license then it'll be the best additional value.
| arrowsmith wrote:
| Presumably the value comes from providing a single unified
| platform that means you don't have to integrate with every
| underlying service separately.
|
| I know nothing about the lower-level details of payment
| networks but the mere fact that this company exists and has
| customers would suggest that there's a value-add.
| conroy wrote:
| In this case, a HTTP API is the abstraction. Integrating with
| ACH and other payment rails requires a lengthy integration
| process. Sometime you have to send binary files using FTP!
| jiggawatts wrote:
| The article says "no abstractions", but HTTP is often exactly
| that: an abstraction over lower-level protocols.
| jackflintermann wrote:
| I guess the phrase "no abstractions" is specifically
| valuable to us when designing our REST API resources - our
| whole stack is certainly an abstraction of sorts, but we
| don't want to add yet another abstraction in that specific
| layer.
| gr4vityWall wrote:
| Just wanted to say that I appreciated the article :)
| Using well-designed APIs is great, and seeing people
| putting a lot of thought on it, with the intent of
| improving dev experience, is very refreshing. I've dealt
| a lot of technically impressive Free Software projects
| that didn't focus on this as much, and as a result, using
| their libraries was harder.
| jackflintermann wrote:
| Thank you!
| chaos_emergent wrote:
| perhaps what you're thinking of is "equal entropy
| abstractions" - HTTP is just a way of standardizing logic,
| but the complexity of the shape and behavior of the API
| remains.
| gr4vityWall wrote:
| My interpretation is that they meant domain-level
| abstractions. So, their API endpoints won't try to hide
| details about the underlying payment methods through
| abstractions, because that works best for those users.
|
| The API being implemented with JSON over HTTP isn't related
| to the domain of processing payments, so I don't see it as
| a contradiction to the article's title.
| contravariant wrote:
| There is abstraction there's just not an additional layer of
| abstractions on top of it.
|
| Which to be honest is quite good, there's lots of things you
| can solve with an additional layer of abstraction but not
| having too many layers of abstraction. It's also rare to be
| able to identify an abstraction that _correctly_ cuts things
| off at the right layer.
| alex_lav wrote:
| > If there's no abstraction, what's your value-add?
|
| But then
|
| > I don't care enough to read
|
| Hmmmmmm.
| summerlight wrote:
| I like the part that explains why Increase choose a different
| approach. Contexts matter a lot when you design something
| fundamental, but people usually don't appreciate this enough.
| kikimora wrote:
| I think this is better than Stripe's abstract everything approach
| even for people who are not into payments. Stripe has built a
| very leaky abstraction.
| adelineJoOs wrote:
| How is the leakage noticeable?
| kikimora wrote:
| I'm not saying Stripe API is bad. But there are limits to how
| much differences you can hide behind a generic API while
| keeping it consistent.
|
| Off the top of my head I can think of a few cases I would
| qualify as a leaky abstraction. To start with - there is a
| payment method abstraction and there is SetupIntent that
| works with it. Normal use case is tokenizing a CC. But for
| ACH it does something different if ever works. Same setup
| intent would work with debit cards, but not in Brazil because
| of local regulations. I don't remember if you get a decent
| error code when attempt to tokenize a Brazilian debit card.
|
| Customers making cards payments can initiate a dispute which
| would cost you 15 usd + payment amount if they win. This
| cannot happen with some other payment methods. It became
| important when you implement Stripe connect because you might
| want to set different fees for different payment methods to
| account for cost of disputes. The leaky abstraction part here
| is as soon as you start creating certain type of payment
| intents you also have to subscribe to Stripe webhooks for
| disputes.
|
| To save on refund fees you may want to authorize payments
| (confirm payment intent) and capture them after a period of
| time. During that window you can cancel the payment and pay
| only authorization fee instead of paying full refund fee.
| This strategy works only for payment methods supporting
| authorization and capture semantics and having favorable
| commission structure. Max amount of time between confirm and
| capture depends on the payment method as well.
|
| Not specific to Stripe Terminals but still. Tapping a card
| gives you an anonymized payment method while dipping the same
| card reveals some cardholder data. This is beyond Stripe
| control, but puzzling at first because at the API level you
| deal generic PaymentMethod object.
|
| With Stripe connect what happens after the payment is defined
| in terms of abstract transfers between Stripe accounts. In
| some regions transfers works across countries while not in
| the others. One example is Canada-USA vs Brazil and rest of
| the world. From one end you have abstract transfers API to
| move money between Stripe accounts. From another you have to
| implement a number of workarounds to make transfers work in
| all interesting scenarios because of regional and currency
| conversion considerations. For example in some cases you do
| transfers while in other you do payment intents.
|
| What I'm trying to say here is you have to know specifics of
| payment methods, underlying technologies and regions you work
| with. By looking at high-level API you may think it is easy
| to support many payment methods when in fact many of them
| would require very specific code.
| l5870uoo9y wrote:
| > Monthly fees for users building on Increase vary by use case.
|
| I am currently adding public API access to AI-powered text-to-SQL
| endpoint with RAG support and the my biggest issue is the
| pricing. Anybody have a ballpark figure what we could be talking
| about here? Pricing must account for OpenAI tokens (or perhaps
| letting them add their own OpenAI token), database usage and
| likely caching/rate limiting setup down the line.
| chaos_emergent wrote:
| Foundationally, pricing should be based on _value_ , not _cost_
| [1] so you should think about what the value is to your
| customer and go from there.
|
| Ex: I know that Gong costs a ton of organizations over
| 100k/year, and there's no way that, accounting for storage,
| CPUs, and all the other OpEx, that the cost comes anywhere
| close to the cost of compute - it's likely at least an order of
| magnitude greater. But because sales teams bring in so much
| revenue so directly, any leverage that they can buy in the form
| of a tool like Gong is immediately and obviously valuable.
|
| [1]: the exception to avoiding cost-plus pricing is if you're
| selling a commodity. But you're not in that boat!
| lolpanda wrote:
| for any APIs related to money, should the currency be in strings
| as opposed to in floats? This will prevent accidental float
| arithmetic in the code. I always find it tricky to work with
| currency in javascript.
| trevor-e wrote:
| I've always seen currencies multiplied by 100 to remove the
| need for floating point.
| nijave wrote:
| Yeah, this seems like a common pattern. Not sure about
| currency with arbitrary place values though (like Bitcoin)
| deathanatos wrote:
| I'm not sure what you mean by "arbitrary place values" with
| Bitcoin; if you are implying it's infinitely divisible, it
| isn't. You'd do the same trick with Bitcoin: represent it
| as an integer1. The value 1 is 1 sat, or 0.00000001 BTC.
|
| 1(where you need to; otherwise, I'd use some fixed point
| type if my language supports it)
| akavi wrote:
| That's not quite a sufficient rule. Eg, 1 Bahraini Dinar is
| 1_000 Bahraini Fils.
| cateye wrote:
| Some currencies use more than 2 decimal places. For instance,
| the currencies of Algeria, Bahrain, Iraq, Jordan, Kuwait,
| Libya, and Tunisia are subdivided into 3 decimals.
| kadoban wrote:
| If you use a higher constant, 10000 or 1000000 or something,
| you give yourself a good amount of more fleixibility.
| freedomben wrote:
| Yes, never use floats for currency. I typically use integers
| and for USD for example, measure in "cents" rather than dollar.
| I try to avoid the fallacy of appeal to authority, but this is
| what Stripe does. You can also use the Decimal type in
| javascript and convert to/from strings to cross API boundaries.
| benhoyt wrote:
| From their docs [1] it looks like they do everything using
| integers: the amounts are integers in the "minor unit" of
| currency, for example cents if the currency is dollars. So 1000
| means $10.00. In languages like JavaScript where everything is
| a float64, you can still accurately represent integers up to
| 2^53, which would be $90 trillion.
|
| [1] https://increase.com/documentation/api#transactions
| crabmusket wrote:
| This isn't sufficient to represent _prices_ which often
| include fractional amounts of cents in non-retail scenarios.
| Think of AWS server prices per hour.
| krainboltgreene wrote:
| Funny, because that's exactly what Stripe does
| (https://docs.stripe.com/billing/subscriptions/usage-
| based/pr...)
| endofreach wrote:
| You should never use floats for dinero. And it has nothing to
| do with JS, though i find it funny that you mention JS.
| xxgreg wrote:
| Don't use floats if you're trying to represent an exact
| value, i.e. someones bank account. But in financial modelling
| you're generally dealing in probabilistic "expected values",
| it's common and fine to use floats.
|
| Having said that, half the world seems to run on Excel
| spreadsheets, which are full of money values, and Excel is
| basically all floats (with some funky precision logic to make
| it deterministic - would be curious to know more).
|
| https://stackoverflow.com/questions/2815407/can-someone-
| conf...
| cratermoon wrote:
| neither. Use rational or some other better type.
| tadfisher wrote:
| I will be the contrarian: JSON numbers are not floating point
| values, they are strings of characters matching the format
| "-?(?:0|[1-9]\d*)(?:\\.\d+)?(?:[eE][+-]?\d+)?". You can choose
| to parse them however you want, and parsing libraries should
| provide a mechanism to decode to an arbitrary-precision value.
| koreth1 wrote:
| By way of example: when I worked on payment code in Java, we
| accepted numeric JSON values in request payloads but they
| were deserialized into "BigDecimal" fields in our payload
| classes, not "float" or "double".
| lolpanda wrote:
| Good point. it's not a problem with JSON. It's just that most
| of the JSON libraries by default parse numbers into floats.
| int_19h wrote:
| Regardless of what the libraries _should_ be doing, there is
| the reality of what they _are_ doing.
| meekaaku wrote:
| Yes, but say in javascript if you do a JSON.parse(), it will
| give you a double float right?
| GeneralMayhem wrote:
| Ideally integers, but at a large multiplier.
|
| Google's money proto [1] has units and nanos. Any competent ad-
| tech system will use something similar: integer number of
| micro-dollars, nano-dollars, etc. You want a fair amount of
| precision, so just tracking whole cents isn't enough, but you
| want that precision to be (linearly) equally distributed across
| the value space so that you can make intuitive guarantees about
| how much error can accumulate.
|
| [1]
| https://github.com/googleapis/googleapis/blob/master/google/...
| andrewstuart wrote:
| I hate abstractions. Program the thing as it is intended.
|
| Why do programmers always need a library between them and the
| API?
| Jtsummers wrote:
| > Why do programmers always need a library between them and the
| API?
|
| You do know that libraries present an API, right? Very few
| people program on Linux or other OSes without using libc or the
| OS/distribution equivalent, and for good reason. Those
| libraries provide a degree of compatibility across hardware
| systems and operating systems (and even the same OS but
| different versions).
|
| Your question is about as sensible as asking "Why do
| programmers always need a programming language between them and
| the machine code?" Because it improves portability,
| reusability, reasonability, and on and on. Though, since you
| hate abstractions, maybe you do only program in machine code.
| koreth1 wrote:
| I kind of hate the fact that the term "API" has lost its
| generality in the minds of a huge number of practitioners,
| and people now assume it refers to a set of network (usually
| HTTP) request and response formats.
|
| It's great that we have a succinct word to describe
| programmatic interfaces built on top of HTTP. It's not great
| that there's no longer a universally-understood word for the
| original more general meaning even though, as this thread
| demonstrates, the original meaning is still as relevant as
| ever.
| compootr wrote:
| I think context has to be taken into account
|
| people here are referring to some financial service on the
| internet, whose API is invoked over http
|
| An article about some library might be viewed differently,
| i.e "X's API is better than Z's"...etc
| cratermoon wrote:
| No Abstractions here really means "just use terms from the
| underlying system", which is a good naming principle in general.
|
| Problems inevitably arise over time when there's multiple
| underlying systems and they have different names for the same
| thing, or, arguably worse, use both use a name but for different
| things. In this example, what if the underlying payment providers
| have different models? Also, what if the Federal Reserve,
| deprecates Input Message Accountability Data and switches to a
| new thing?
|
| Maybe things are a lot simpler in the payment industry than they
| are in transportation or networking protocol. If I built a
| packet-switching product based on X.25 and later wanted to also
| support tcp/ip, what's the right abstraction?
| advisedwang wrote:
| > No Abstractions here really means "just use terms from the
| underlying system"
|
| The article clearly says it also means "no unifying similar
| objects", which enables the naming decision.
| cratermoon wrote:
| How does that work if, for example, the example given of
| "Visa and Mastercard have subtly different reason codes for
| why a chargeback can be initiated, but Stripe combines those
| codes into a single enum so that their users don't need to
| consider the two networks separately.". Unfortunately, the
| article doesn't explain how Increase handles that overlap.
| Presumably, as the article states, their customers are the
| sort that _do_ care about Visa reason codes vs Mastercard
| reason codes, so what 's the design of a "no abstraction" API
| in that case?
| travisjungroth wrote:
| I'm just reasoning from my limited experience and the
| article. Stripe unifies those reasons codes. Increase
| doesn't. It might be that the Chargeback has the processor
| and chargeback code as attributes.
|
| So rather than have a universal "goods and services not
| received", it's a 13.1 for Visa, a 4855 for MasterCard and
| a F30 for Amex. This matters when the boundaries are
| different. For example, they all split up the categories of
| fraud differently.
| jackflintermann wrote:
| I appreciate the thorough read!
|
| For deprecations we're lucky in that the underlying systems
| don't change very much (the Input Message Accountability Data
| isn't going anywhere). But we'll run into collisions when we,
| for example, start issuing cards on Mastercard as well as Visa.
|
| We have experimented with a couple of, um, abstractions, and
| may do so there. One rule we've stuck to, and likely would as
| well, is to keep the "substrate objects" un-abstracted but to
| introduce higher-level compositions for convenience. For
| example, there is no such thing as a "Card Payment"
| (https://increase.com/documentation/api#card-payments) - it's
| just a way to cluster related card authorization and settlement
| messages. But it's _extremely_ useful for users and nontrivial
| to do the reconciliation, so we tried it. But we think it 's
| essential that the underlying network messages (the "substrate
| objects") are also accessible in the API, along with all the
| underlying fields etc.
|
| Unfortunately 100% of the public APIs I have worked on are in
| payments. I wish I had another lens!
| Terr_ wrote:
| > No Abstractions here really means "just use terms from the
| underlying system"
|
| Which sounds a bit like Domain Driven Design, although the
| "underlying system" in this case may be a bit too
| implementation-centered to be considered a real business
| domain.
|
| To expand on that a bit: In DDD you generally defer to the
| names and conceptual-models the business-domain has already
| created. Trying to introduce your own "improved" [0] model or
| terms creates friction/miscommunications, adds opportunities
| for integration bugs, and ignores decades or even centuries of
| battle-tested specialized knowledge.
|
| [0] https://xkcd.com/793/
| cratermoon wrote:
| > the "underlying system" in this case may be a bit too
| implementation-centered to be considered a real business
| domain.
|
| I tend to agree with this. The domain concepts would be
| things like charge-backs and the reasons for them. The
| details of the codes and categories are implementation-
| specific. Unless, as Increase seems to be implying, their
| domain _is_ the payment networks and fintech and their
| customers care about them the same way a kernel programmer
| would care about the details of memory allocators or
| schedulers, while most application programmers just want them
| to exist and work in a consistent way.
| cpeterso wrote:
| This is similar to Domain-Driven Domain's "Ubiquitous Language"
| design pattern, making your implementation use the same real-
| world terminology used domain experts.
|
| https://thedomaindrivendesign.io/developing-the-ubiquitous-l...
| hinkley wrote:
| I was introduced to this concept a good while before DDD came
| along, when someone opined that if the nouns and verbs in your
| code don't match the problem domain that's an impedance
| mismatch and it's going to get you into trouble some day.
|
| It really reads like a shame response to me. People are so
| pathologically allergic to saying "I was wrong" or "we were
| wrong" that they end up pushing their metaphors around like a
| kid trying to rearrange their vegetables on their plate to make
| it look like they ate some of them.
|
| It's also smacks of the "No defects are obvious" comment in
| Hoare's Turing Award speech.
| Karellen wrote:
| > If you're building an abstraction-heavy API, be prepared to
| think hard before adding new features. If you're building an
| abstraction-light API, commit to it and resist the temptation to
| add abstractions when it comes along.
|
| You could always do both.
|
| Provide a low-level abstraction-light API that allows fine
| control but requires deep expertise, and write a higher-level
| abstraction-rich API on top of it that maps to fewer simple
| operations for the most common use cases - which some of your
| clients might be implementing their own half-baked versions of
| anyway.
|
| If you maintain a clean separation between the two, having both
| in place might mean there is less pressure to add abstractions to
| the low-level API, or to add warts and special-cases to the high-
| level API. If a client wants one of those things, it already
| exists - in the other API.
|
| Bonus points for providing materials to help your clients learn
| how to move from one to the other. You can attract clients who do
| not yet have deep knowledge of payment network internals, but are
| looking to improve in that direction.
| paulddraper wrote:
| Git is an example of this. [1]
|
| There are high-level "porcelain" commands like branch and
| checkout.
|
| And then there are low-level "plumbing" commands like commit-
| tree and update-ref.
|
| [1] https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-
| Po...
| mbork_pl wrote:
| Came here to say that.
|
| Also, to some extent, Emacs. There are thousands of
| _functions_ (actually, a bit less than 10k in stock Emacs
| without packages, and over 46k in _my_ Emacs) performing
| various low-level tasks, and much fewer _commands_ (~3k in
| stock Emacs, almost 12k in my config), i.e., interactive
| functions, often higher-level, designed for the user.
| yen223 wrote:
| It does double your API surface area, so that's the tradeoff
| you'll have to consider. It can be the correct decision in a
| lot of cases.
| Pxtl wrote:
| It doesn't double the security surface area if the abstracted
| API goes through the low-level API. The outer one is just
| chrome, and so the risks of screwing something up there is
| far lower.
|
| Unless you're using a trash language where even simple
| wrappers could buffer underrun or something.
| lmz wrote:
| Isn't there the issue of modifying the state enough through
| the low-level API such that it breaks the assumptions of
| the high-level one?
| eru wrote:
| Just say that you don't support mixing both APIs.
| actionfromafar wrote:
| That'll fix it. ;-)
| lazyasciiart wrote:
| Now you're telling users to check the source code of any
| tools they use with your stuff? "We recommend using
| ToolOne only on content you do not also manage with
| ToolTwo"
| withinboredom wrote:
| The high level API shouldn't care about state. In other
| words, your "readers" should merely aggregate state and
| your "writers" should only care about subsets of state.
|
| Think about file permissions in Linux. Running ls just
| shows you gross file perms (current user, group, and
| global) but you can also grant access to other individual
| users, or even make a file immutable that still shows up
| as writable to ls. The high level api doesn't know or
| care about the low level state except where it is
| relevant.
| Pxtl wrote:
| Yes. But that's the high-level API's problem. That's a
| problem with any abstraction really. "What if there's
| something in the thing we're abstracting that doesn't fit
| the abstraction" isn't really a problem with the "two
| API" approach, it's a problem with abstraction.
|
| The high-level API needs to handle that case, if nothing
| better than having internal assertions that throw if it
| hits a case it's not designed to accommodate.
|
| (also I'm annoyed with myself that I wrote buffer
| underrun in my first post instead of buffer overflow and
| now it's too late to edit).
| campbel wrote:
| Unlikely to double. The low level API exposes all
| capabilities, the high level API exposes a subset of those
| capabilities under a smaller surface. The high level API will
| not be as large as the low level.
| blowski wrote:
| This reminds me of the Kubernetes API.
| IggleSniggle wrote:
| This reminds me of the git API.
| jampekka wrote:
| This. There should be a low level API to be able to do rarer
| more complicated cases, and a higher level simple API for
| common cases built on the lower-level API.
|
| Just today I was working with the Web File System API, and e.g.
| just writing a string to a file requires seven function calls,
| most async. And this doesn't even handle errors. And has to be
| done in a worker, setting up of which is similar faff in
| itself. Similar horrors can be seen in e.g. IndexedDB, WebRTC
| and even plain old vanilla DOM manipulation. And things like
| Vulkan and DirectX and ffmpeg are even way worse.
|
| The complexity can be largely justified to be able to handle
| all sorts of exotic cases, but vast majority of cases aren't
| these exotic ones.
|
| API design should start first by sketching out how using the
| API looks for common cases, and those should be as simple as
| possible. E.g. the fetch API does this quite well.
| XMLHttpRequest definitely did not.
|
| https://developer.mozilla.org/en-US/docs/Web/API/FileSystemS...
|
| Edit: I've thought many a time that there should be some
| unified "porcelain" API for all the Web APIs. It would wrap all
| the (awesome) features in one coherent "standard library"
| wrapper, supporting at least the most common use cases. Modern
| browsers are very powerful and capable but a lot of this power
| and capability is largely unknown or underused because each API
| tends to have quite an idiosyncratic design and they are
| needlessly hard to learn and/or use.
|
| Something like what jQuery did for DOM (but with less magic and
| no extra bells and whistles). E.g. node.js has somewhat
| coherent, but a bit outdated APIs (e.g. Promise support is
| spotty and inconsistent). Something like how Python strives for
| "pythonic" APIs (with varying success).
| metalspoon wrote:
| Tbf Vulkan is not intended for an endprogrammer. It is a
| deliberately low level standardization to allow directly
| control GPU hardware. The high-level approach (OpenGL)
| failed. The endprogrammer is supposed to use a third party
| middleware, not Vulkan itself.
| withinboredom wrote:
| Someone has to write that middleware...
| f1shy wrote:
| So OpenGL is a total failure! I learned something today...
| What should I use?
| wizzwizz4 wrote:
| I like WebGPU: it's slightly higher-level than OpenGL,
| but the shader language is better. (For the actual web,
| use WebGL: like Wasm, WebGPU isn't actually suitable for
| use in websites.)
| dumbo-octopus wrote:
| It's 4 async calls, and it can be done entirely in the main
| thread so long as you use the async API.
| https://developer.mozilla.org/en-
| US/docs/Web/API/FileSystemW...
| jampekka wrote:
| For OPFS it's still 6 calls. And the async API doesn't
| support flushing writes.
|
| https://developer.mozilla.org/en-
| US/docs/Web/API/File_System...
| cpeterso wrote:
| My favorite quote about abstraction is from Edsger Dijkstra:
|
| _"The purpose of abstraction is not to be vague, but to
| create a new semantic level in which one can be absolutely
| precise."_
|
| If only the "A" in API stood for "abstraction". For many
| APIs, it probably stands for "accreted". :)
| InvisibleUp wrote:
| .NET also does this a lot. Here's a recent devblog post looking
| at file I/O: https://devblogs.microsoft.com/dotnet/the-
| convenience-of-sys...
| eterm wrote:
| That's a fantastic blog post, worthy of it's own HN
| submission.
| Groxx wrote:
| I'm particularly fond of this pattern when you can implement
| the high level API that you want _outside the library_ , which
| ensures that your low level API is sufficiently flexible _and_
| means you 're dogfooding your own stuff as a user. It's far too
| easy to get used to the internal side of a tool you're
| building, and forget how people actually use it.
| HelloNurse wrote:
| It is also important to guarantee that the two API designs
| are coherent and interoperable, and this kind of strict
| layering is the best strategy to avoid mistakes.
| andenacitelli wrote:
| Another example of this is AWS CDK. There are a few "levels" of
| constructs - high level ones that are simpler and apply
| "presets" that are enough for 80% of users, but the core low
| level ones still exist if you have a nonstandard use case.
| zellyn wrote:
| Problems can sneak in when you use the low-level API to do
| something to an object that can't be cleanly represented in the
| higher-level API. You need some kind of escape hatch, like a
| list of links to or ids of low-level details or a blob
| (Map<String,arbitrary-JSON> of miscellaneous data that can hold
| the low-level additions.
|
| Hopefully the top-level important concepts like "amount_due"
| will still reflect the correct numbers!
| withinboredom wrote:
| Those problems usually present themselves by people
| overthinking the high level api and trying to be smart.
|
| As an example, you can use chattr to make a file in Linux
| immutable. ls still shows that you have permission to write
| to the file, even though it will fail.
|
| When people try to overthink the api and have it determine if
| you really can write to a file, people will try using the
| high level api first (chmod) and it won't work because it has
| nothing to do with permissions.
|
| KISS is really needed for high level APIs.
| eru wrote:
| Compare also exokernels, and how they delegate abstraction to
| libraries, not the OS.
| lwhi wrote:
| I like this idea a lot.
|
| One level of API for implementation model.
|
| And second level for mental model.
| devjab wrote:
| I'm genuinely curious as to what an abstraction-rich api would
| look like and why it would be useful.
|
| I've mainly worked in enterprise organisations or in startups
| transitioning into enterprise which is sort of where my
| expertise lies. I've never seen an API that wasn't similar to
| the examples in this case.
|
| I mean... I have... but they wouldn't be labelled as high-
| abstraction api's. If they needed a label it would be terrible
| APIs. Like sending table headers, column types in another full
| length array with a line for each column, and then the actual
| data/content in a third array. Even sending the html style that
| was used in what ever frontend, so that some data is
| represented as "some data" and other is represented as ["some
| data", ["text-aligned", "center"...],... . Yes, that is an
| actual example. Anyway I've never seen a high abstraction api
| and I feel like I'm missing out.
| jimbob45 wrote:
| That sounds like the exact philosophy the Vulkan devs took
| versus OpenGL.
| lwhi wrote:
| So they say parts of the API structure are based 1-1 on
| externally controlled specifications.
|
| What happens if those specifications evolve or change?
|
| New API?
| Supermancho wrote:
| https://docs.stripe.com/api/versioning
|
| A versioned API. Which means more APIs to maintain, until they
| are removed.
| bvrmn wrote:
| Also it greatly helps to not overuse nouns and try to forcefully
| model verbs with resource entities.
| west0n wrote:
| If we didn't have abstractions like POSIX, applications would
| need to write an adaptor for every supported file system.
| shironandonon_ wrote:
| TLDR: we are special and created our own standard.
| ngrilly wrote:
| They are saying the opposite: we follow existing standards as
| much as possible.
| dheera wrote:
| Java engineers need to see this. Goddamn Proxies, Factories, and
| Beans. Fragments, Surfaces, Runnables.
| theptip wrote:
| This is a great example of the concept "ubiquitous language" from
| Domain Driven Design.
|
| Use language that your domain experts understand. If your users
| know about NACHA files, using other terms would mean they need to
| keep a mapping in their head.
|
| On the other hand, in Stripe's case, their users are not domain
| experts and so it is valuable to craft an abstraction that is
| understandable yet hides unnecessary detail. If you have to teach
| your users a language, make it as simple as possible.
| Nevermark wrote:
| Or to put it another way, they are domain experts in the kinds
| of transactions they want to perform, not how transactions are
| implemented in the financial system.
| tegling wrote:
| One tricky thing to model neatly in payment APIs is that payment
| schemes indicate the roles of payer and payee in payment returns
| in different ways. E.g. for one particular scheme the payer and
| payee may be kept in the same position as in the initial payment
| (creditor of payment return is actually the one sending funds)
| whereas in another one they are switched (creditor is the one
| receiving funds in the return). I'd be curious to see how they
| are handling this case as it can be a real head-scratcher.
| danecjensen wrote:
| Strong work Jack!
| RobotToaster wrote:
| No abstractions? So their API lets me control the individual
| registers on their CPU then?
| dkjaudyeqooe wrote:
| No, sorry, registers are an abstraction.
|
| The API only lets you set voltages on individual wires.
| the_af wrote:
| Interesting.
|
| The title of the concept is misleading, "No Abstractions" here
| doesn't literally mean "no abstractions" but instead "we use this
| specific set of abstractions, and not others". And the specific
| subset they describe is worth discussing! But it's of course a
| set of abstractions.
|
| E.g.
|
| > _For example, the parameters we expose when making an ACH
| transfer via our API are named after fields in the Nacha
| specification_
|
| A specification is an abstraction.
|
| > _Similar to how we use network nomenclature, we try to model
| our resources after real-world events like an action taken or a
| message sent. This results in more of our API resources being
| immutable [...] and group them together under a state machine
| "lifecycle object"._
|
| Immutability (in this sense) and "lifecycle objects" are
| abstractions.
|
| > _If, for a given API resource, the set of actions a user can
| take on different instances of the resource varies a lot, we tend
| to split it into multiple resources._
|
| Another abstraction, just splitting at a different level than the
| Stripe API.
|
| This is a set of design decisions and abstractions. Definitely
| not a "no abstractions" principle. I would say the most important
| decision they seem to have made is to generalize as little as
| possible -- and generalization is indeed a kind of abstraction.
| Maybe "Fewer Generalizations" would have been a more accurate
| title?
___________________________________________________________________
(page generated 2024-04-26 23:02 UTC)