[HN Gopher] No Abstractions: our API design principle
___________________________________________________________________
No Abstractions: our API design principle
Author : jackflintermann
Score : 150 points
Date : 2024-04-25 19:15 UTC (3 hours 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
| 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.
| 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.
| 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.
| 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?
| 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.
| 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.
| 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 our API but they were
| deserialized into `BigDecimal` fields in our payload classes,
| not `float` or `double`.
| 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.
| 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?
| 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!
| 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...
| 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...
| 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?
___________________________________________________________________
(page generated 2024-04-25 23:00 UTC)