Skip to content

Instantly share code, notes, and snippets.

@piotrpalek
Last active December 21, 2020 12:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save piotrpalek/c7076c915fbfb7c9472b43e6e195f297 to your computer and use it in GitHub Desktop.
Save piotrpalek/c7076c915fbfb7c9472b43e6e195f297 to your computer and use it in GitHub Desktop.
hnAPI
import Ember from 'ember';
export default Ember.Controller.extend({
store: Ember.inject.service(),
init() {
this._super(...arguments);
this.set('isLoading', true);
this.get('store').cachedStories(700).then((stories) => {
this.set('stories', stories);
this.set('isLoading', false);
});
}
});
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route('stories');
});
export default Router;
import Ember from 'ember';
let cachedStories;
//naive HNApi wrapper
export default Ember.Service.extend({
fetch(url) {
return new Promise((resolve, reject) => {
$.getJSON(url)
.done((json) => resolve(json))
.fail((xhr, status, err) => reject(status + err.message));
});
},
hnUrl(url) {
return `https://hacker-news.firebaseio.com/v0/${url}.json`;
},
fetchHn(url) {
return this.fetch(this.hnUrl(url));
},
topStories(count = 10) {
return this.fetchHn('topstories').then((itemIds = []) => {
let storyPromises = itemIds.slice(0, count).map((itemId) => this.getItem(itemId));
return Promise.all(storyPromises);
});
},
topStoriesWithComments(count = 3) {
return this.topStories(count).then((stories) => {
let storyPromises = stories.map((story) => {
return new Ember.RSVP.Promise((resolve, reject) => {
let kids = story && story.kids || [];
let comments = kids.map((commentId) => this.getItem(commentId));
let commentPromises = Ember.RSVP.all(comments);
return commentPromises.then((comments) => {
resolve({
text: story.text,
title: story.title,
comments: comments.mapBy('text')
});
}).catch(reject);
});
});
return Promise.all(storyPromises);
});
},
getItem(itemId) {
return this.fetchHn(`item/${itemId}`);
},
cachedStories(delay = 600) {
return new Ember.RSVP.Promise((resolve) => {
setTimeout(() => resolve(cachedStories), delay);
});
}
});
cachedStories = [{
"title": "Twice as happy customers means half the marketing spend",
"comments": ["I love this post - it gets across a fundamental concept that pretty much anyone can understand, which is non-obvious.<p>What I love, that other&#x27;s here seem to grumble about, is that he&#x27;s not using any specific terminolgy. He doesn&#x27;t mention &#x27;churn&#x27;, he doesn&#x27;t mention &#x27;geometric&#x27; series, he only has one &#x27;=&#x27; in the whole text.<p>You can print out this article (ignoring the python bit) take this down to your local baker&#x2F;cafe&#x2F;&lt;insert non-tech business owner&gt; that&#x27;s full of stupid marketing billboards but sucks at customer service and show it to them. There&#x27;s a good chance they&#x27;ll understand.<p>Frankly I liked it because I could easily follow his logic without slowing my reading. There was parity between my understanding speed vs my reading speed.<p>Plus no-one here seems to be grumbling that he&#x27;s <i>wrong</i>, just that they can say it in a different way.", "The falls out of the fundamental equation for SaaS and other subscription business models:<p>LTV = prospects * (conversion rate to paying) * (average price point) &#x2F; churn<p>This makes a 5% increase in prospects, conversion rate, or price cause a 5% increase to LTV (and, eventually, to the enterprise value of the company). A 5% decrease in churn (measured against one&#x27;s current churn rate, e.g., 5% -&gt; 4.75%) has a slightly more than 5% impact to LTV &#x2F; enterprise value.<p>So many decisions about running a SaaS company fall directly out of this equation. Competent SaaS operators memorize it or, for less mathematically oriented operators, can at least summarize the relationships implied.", "I think this is highly relevant for the early stages of a product: Do you focus on retention or marketing? I&#x27;m developing a cross-platform file manager [1]. My current problem is that even tough 20 people download it every day, only 2 use it more than once in the first week. Existing users ask me for 1000s of features. But the real problem I need to overcome for growth is that I lose so many first-time users. That&#x27;s a problem with onboarding, not with features. I blogged about this [2].<p>[1]: <a href=\"https:&#x2F;&#x2F;fman.io\" rel=\"nofollow\">https:&#x2F;&#x2F;fman.io</a><p>[2]: <a href=\"https:&#x2F;&#x2F;fman.io&#x2F;blog&#x2F;desktop-app-funnel-optimization&#x2F;\" rel=\"nofollow\">https:&#x2F;&#x2F;fman.io&#x2F;blog&#x2F;desktop-app-funnel-optimization&#x2F;</a>", "I was a customer of Candy Japan for a while (3 months) and I really liked it. The candy selection was very interesting and fun. A while back I tried a competitor and their selection was very boring and the candy they sent didn&#x27;t feel very special.<p>The reason I cancelled with Candy Japan was that I found it a lot of money for candy. Not so much a lot of money for the service. Also, candy is just unhealthy, so I am probably better of spending that amount on fruit and vegetables.<p>Overall the candy selection is what would make me choose Candy Japan over a competitor if I would ever choose a candy service again or recommend one to friends.", "Yeah, that&#x27;s the Churn Equation. The thing us SaaS folks spend most of our time worrying about (whether we know it or not).<p>Given a constant influx of potential customers at a known conversion rate, along with a known churn rate of existing customers, you can find an exact dollar figure that you will eventually plateau at.<p>It sucks. Especially when you&#x27;re starting out because the &quot;In&quot; side of the equation is small and there&#x27;s not much you can do about it.<p>Fortunately, as the article points out, there are a few knobs you can tweak. Churn is a nice one, since all it takes is a good product. Conversion is harder, because it involves dark magic like sales and psychology and web design skills.<p>Once you get it figured out, though, there&#x27;s another formula you can use to determine how much you&#x27;re allowed to spend to pour one new user into the top of your Trial funnel. If you can get that up to a level that justifies advertising, you can open the valve as far as your budget allows and start moving that plateau point upwards.<p>EDIT: I&#x27;ve been writing about this stuff lately, if anybody else likes geeking out on it:<p><a href=\"http:&#x2F;&#x2F;www.expatsoftware.com&#x2F;Articles&#x2F;things-worth-knowing-about-your-trial-users.html\" rel=\"nofollow\">http:&#x2F;&#x2F;www.expatsoftware.com&#x2F;Articles&#x2F;things-worth-knowing-a...</a>", "This really overcomplicates the mathematics.<p>Steady state implies Leavers = Joiners.<p>If 100 people join each month then you&#x27;ll have steady state when there are 100 leavers, i.e. when 50% of N is 100. N * 50% = 100 solves to N = 200.<p>In general with X% attrition and Y people joining you&#x27;ll have a steady number of subscribers N using the formula<p>N * (1 - X) + Y = N<p>Which can be rearranged to N * X = Y.", "A practical application of a geometric series [1].<p>Before my study of statistical distributions I once re-invented the Poisson distribution. It took one look of my physicist colleague to give everyone a good laugh. That was the moment I decided an economists needs more than linear models, the normal distribution and some non-parametric tests.<p>[1] <a href=\"https:&#x2F;&#x2F;en.m.wikipedia.org&#x2F;wiki&#x2F;Geometric_series\" rel=\"nofollow\">https:&#x2F;&#x2F;en.m.wikipedia.org&#x2F;wiki&#x2F;Geometric_series</a>", "&gt; <i>For simplicity suppose 50% of people cancel every month. That means that if I do some clever marketing and manage to get 100 new people to join, then after a month 50 of those would be left. After another month, 25 of those would be left and so on.</i><p>&gt; <i>Because of this fall-off, even if you run a subscription business forever, you will not have infinite customers. Instead you reach a steady-state number.</i><p>You should also factor in returning business as well as new business. eg I&#x27;m a very happy customer but since this is a luxury it is often the first thing I cancel whenever money gets tighter. For example when moving house or, most recently, when my wife gives birth to our new baby girl. However when finances stabilize again Candy Japan is often one of the first luxuries I re-subscribe to again.<p>I&#x27;d also like to take this opportunity to say thank you for providing such a great service. The extra effort you put in with regards to the emailed descriptions of the candy as well as the mixture of normal and alternative snacks make it an absolute delight receiving the box.", "<p><pre><code> Even if you run the business for a million years,\n you will still only have 200 members.\n</code></pre>\nIf the churn rate is steady. It usually goes down over time though. Because long time customers have a lower likelihood of canceling.<p>In other words: The past does not equal the future. If you look back on your business and see a churn rate of X%, you can expect a lower churn rate in the future. Because the &quot;survivors&quot; will have a lower churn rate then new customers.<p>In e-commerce, this effect often is pretty significant. I know multiple online-shops that make the majority of their business with long time customers. Even though over 50% of new customers drop out after the first month.", "Good post, but it does not consider conversion of former customers.<p>I develop a subscription service which launched in January and now has ~9,000 MAUs. This above metric accounts for ~10% of &quot;new&quot; subscribers each month, despite not yet making an effort (such as by sending reminder or &quot;sorry&quot; emails) to re-capture them.<p>The post does make a good point about retaining existing users, though.<p>I would like to add that we saw MAUs spike when redesigning the cancellation process to be more thankful and apologetic than spiteful. (Being a service for Japanese users, we included a little &quot;thank you bow&quot; animated character at the end of the process.)<p>One more point – our cancellations for the first days of the month often come close to outnumbering new users. We have concluded that the reason for this is that users 1. perform their financial housekeeping around this time, and 2. find a low-numbered charge date easier to remember.", "What are your thoughts about Amazon moving into the subscription box business?<p><a href=\"https:&#x2F;&#x2F;www.amazon.com&#x2F;b?node=14809330011\" rel=\"nofollow\">https:&#x2F;&#x2F;www.amazon.com&#x2F;b?node=14809330011</a>", "The reason developers don&#x27;t use math is because it never fits the real world models.", "There&#x27;s an old saying: It&#x27;s a lot more expensive to acquire a new customer than to keep an existing one from leaving.", "Implicit here is an assumption that the lifetime of subscribers is exponentially distributed. Which may or may not be the case.", "Main takeaway:\nThe function f(x) = 0.5*x + 100 has a fixed point at x = 200.", "I don&#x27;t suppose CandyJapan has something for the lactose intolerant? :)", "100+50+25= 175", "Python and first checked the previous result"]
}, {
"title": "Typing Nix",
"comments": ["I love Nix. NixOS is my primary development operating system. However, the Nix language itself is syntactically <i>ugly</i>, and this proposal makes it even uglier. Parentheses, sigils and special characters (esp. semicolons) are line noise — the less of it the better.<p>Why they wouldn&#x27;t take the most (syntactically) beautiful functional programming language out there — Standard ML? It would work perfectly for such a task. Or the second contender — Haskell. If C-like syntax is desired, the best contendant is probably Swift.<p>I cringe every time I have to edit a .nix file (and I have to do it a lot).", "Neat. Somewhat related is hnix [0], a community effort in re-implementing the Nix expression language in Haskell.<p>While so far the focus has mostly been on implementing the expression language as is, hnix being written in Haskell could be attractive for enriching the language with fancier features:<p>&quot;<i>Because now that it&#x27;s in Haskell, now that a lot of other hackers could get involved, we could do things like add optional typing to the Nix language.</i>&quot; — J. Wiegley<p>For more on hnix check out Haskell Cast&#x27;s Episode 13 [1] with John Wiegley.<p>[0]: <a href=\"https:&#x2F;&#x2F;github.com&#x2F;jwiegley&#x2F;hnix\" rel=\"nofollow\">https:&#x2F;&#x2F;github.com&#x2F;jwiegley&#x2F;hnix</a><p>[1]: <a href=\"http:&#x2F;&#x2F;www.haskellcast.com&#x2F;episode&#x2F;013-john-wiegley-on-categories-and-compilers\" rel=\"nofollow\">http:&#x2F;&#x2F;www.haskellcast.com&#x2F;episode&#x2F;013-john-wiegley-on-categ...</a>", "There&#x27;s also Guix in case anyone is interested. It uses Guile&#x2F;Scheme for everything instead of Haskell.", "Funny: Nix is short (or colloquial) for &quot;nichts&quot; in German which further means &quot;nothing&quot; in English.<p>So the CTA &quot;Get Nix&quot; is kinda funny for German speaking people :)<p>PS: Greetings from Austria!", "Nice.<p>Half-OT: How is the new CLI coming along? AFAIK there was an effort to make Nix usage a bit more like known from other package managers."]
}, {
"title": "Algorithmia raises $10.5M Series A round led by Google’s new AI fund",
"comments": ["Ah at first I thought this meant google had a fund whose investment choices were being completely controlled by an AI. That would have made for a much more interesting story, oh well.", "This is very much inline with the news that Google (Cloud) acquired Kaggle, that shows Google&#x27;s interest in developing ML&#x2F;AI products and deploy them and also offer AI as a Service for those who don&#x27;t have AI background!"]
}];
<header>
{{#if isLoading}}fetching data...{{/if}}
</header>
{{!--
template variable:
stories = [ StoryObj, StoryObj, StoryObj, ...] // array with Story Objects (see below)
StoryObj = {
title: String // hacker news story title
text: String // description of the story
comments: [ String, String, ... ] // array with comments
};
--}}
<article>
{{stories.length}}
</article>
{
"version": "0.12.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.12.0",
"ember-template-compiler": "2.12.0",
"ember-testing": "2.12.0"
},
"addons": {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment