Hacker News new | past | comments | ask | show | jobs | submit | IsaacSchlueter's comments login

100%. The main reason we created https://priceops.org is that there's a real mind-shift that has to happen for someone to realize that there's even a "there" there. Most people who've been through it understand all too well, but even there, it's tempting to think "Oh, if only we'd just committed ignorantly to a better pricing model..." not realizing that you'll always get it wrong and the context will always be changing, so it's critical to have an implementation that can respond to new information.

> 5. Pray that no one ever has to touch it again. (Or failing that, hope you've got a new job somewhere else before that happens.)

So many otherwise smart people make this foolish bet. It is an exceptionally long shot.

> Of course I don't wish them too much success, since I'd like to have some customers too.

Feeling is mutual, I'm sure <3 I think it's potentially a very big space with a lot of work to be done, so there's room for plenty of players in it.


https://priceops.org/ looks a lot like https://12factor.net/ I guess on purpose and by culture.


> written entierly by Isaac

I see what you did there ;)


I enjoy tutorials like this, in the same way and for the same reasons that I enjoy videos where people make cutting tools out of obsidian that they chip by hand.

Personally, though, when I have to actually cut something, I use stainless steel knives or scissors, which are cheap and readily available and do a much better job. Same with pull requests. But historical reenactment can be a fun pastime.


Many startups will go out of business because they don't address this concern.

Startups have extremely little information to guide their pricing and packaging decisions, and often find right away that they've just picked the wrong way to monetize.

An optimal solution makes it easy to build a flexible system, so that it can change rapidly as new information becomes available.


Take the data point for what it's worth (ie, not much). I have never found these chat popovers useful, I have always found them obstructive, and I have always been seething with resentment when forced to use one for an interaction with a company. It's just slightly less painful than being forced to use ye olde telephone, and I will only use live chat if that is the only other option. Most likely, I will simply give up and use a competitor instead.

In any chat I am forced to use, I guarantee I will do my absolute best to radicalize the chat employee against your company and try to convince them to get a different job, or quietly and safely sabotage their employer if they cannot leave. I will make the interaction take as long as possible while doing this, in an effort to drive up costs as much as possible.

I am not alone.


I just played around with the NextJS starter demo app, and it seems like you can `import tier from 'tier'` and call `tier.subscribe()`, `tier.report()`, and `tier.limits()` without any issue in a NextJS API route.


I am not super familiar with NextJS, but I'll take a look.

The Node SDK is a pretty standard Node library (not clientside JS, for probably obvious reasons). It does spawn a child process if it isn't given a TIER_SIDECAR environment variable, though, which is going to be pretty slow if you have stateless route handlers at the edge.

I'll poke at it today, but I think the way to go is to spin up a sidecar running somewhere with `tier serve`, and then give your NextJS API handlers an environment variable to know where to hit it.


> You see a hundred blog posts of "why you should experiment with your pricing", but actually doing that is often such a pain that nobody ends up doing that (and admittedly the reason why I would have shied away from such projects in the past).

Yes, exactly. Especially when those same blog posts tell you to plan to basically have your whole team focus on it for 3 months, as if you have nothing else to do!

> - What are your plans like regarding the integration of entitlement checking in the service and SDK? As far as I can tell, that's still a missing piece? I really love how oso[0] has managed to separate the authorization checking from the authorization definition via their DSL and SDKs. Ever since I saw that, I was wondering if a very similar system could work for entitlement checking.

The SDK does expose a `tier.limit(org, feature)` method[1]. This is reasonably fast, but if it's uncached, it is an API call to Stripe, so it can be beneficial to have that drive a proper feature-flagging system like Launch Darkly, or whatever else you use to manage authorization and feature availability.

> - I think where your system could provide a lot of benefit would be in help tracking usage metrics that can be harder to calculate (and with that more of a pain to use). It's already nice not having to implement a simpler "number of action N taken the last billing period" metric. However something like a "N number of active users last billing period", where you now would also have to keep track of which users you have already seen before to prevent double-counting them becomes increasingly annoying to implement.

For seats you could use an `"aggregate": "perpetual"` setting on the feature. See: https://www.tier.run/docs/recipes/#simple-per-seat-pricing (that page also has some other examples that might be helpful.) With that, you'd just report the increase or decrease every time the seat count changes, and the counter would never be reset. (There's a few other ways to approach it, but that's what I'd do, as it's probably the simplest.)

You're right, though, if you want to charge based on active users, then it gets a bit more tricky, because you do have to avoid double-counting. And the precise definition of "active" becomes really important. I'd probably approach it by putting a "lastSeen" timestamp on each user account, and then periodically calling `tier.report(org, "feature:activeusers", numberActiveSinceFirstOfMonth, Date.now(), true)` to clobber any previous value. (`clobber` is not the default, since usually you want Tier to count it for you.) I'll add an "active users" example to that recipes page.

[1]: https://www.npmjs.com/package/tier#user-content-limitsorg


Just to clarify:

> This is reasonably fast, but if it's uncached, it is an API call to Stripe

It will be cached after the first request you make with the sidecar/SDK, and unless you have a truly impressive number of users, it'll be able to fit your entire data set in local cache and update only when needed, even on a pretty tiny VM.

I just tend to be probably over-cautious when relying on caches to be fast. Fast 99% of the time is slow 1% of the time. Still good to do it, of course, but always a good idea to at least consider what your worst-case will be.


I completely agree. Whoever[1] made that page should probably not be allowed to make web pages without the assistance of a real designer.

[1]: me. I'm talking about me.


I don't think that's 100% accurate implementation-wise, but the gist is definitely correct. Basically, yes, owing to the way that these things have to get mapped into Stripe's objects, having a plan without any features would make it really costly to reconstruct the model later, and we wouldn't be able to create a subscription to it anyway.

You can create a plan like:

    "plan:nofeatures@0": {
      "feature:donotuse": {}
    }
and then the customer subscribed to that plan will have a single $0 item in that subscription phase. (Tier could in theory do something like this automatically if there's a plan with no features, but figured since an empty plan is likely a mistake anyway, better to just let it be explicit.)

To the parent comment, there's nothing in PriceOps that theoretically says a plan must have features, this is just an implementation detail that's somewhat unavoidable.


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

Search: