Ask HN: Looking for Scala + Akka + Spray contractor for startup? - <AUTHOR>

We are a group of 6 working on a startup project with 3 SW engineers working part time but need more Scala/Akka/Spray experienced programmer consultant to help roll out a bare bones server app.  We need a good foundation to build on however and highly scalable. Luckily our senior SW engineer knows what he wants to build, lacks the time.<p>What would be best place to find contractor help? Anyone interested? <comment> eliah-lakhin I don't have experience with Akka specificially, but I have long experience with Scala, and similar web frameworks in another languages.

Here is one of my projects in Scala: [https://github.com/Eliah-Lakhin/papa- carlo](https://github.com/Eliah-Lakhin/papa-carlo) And this is my resume: [https://dl.dropboxusercontent.com/u/26689230/cv.pdf](https://dl.dropboxusercontent.com/u/26689230/cv.pdf)

I would like to work in Akka based startup as a remote full-time contractor(I had been working remotely for years). As a a middle-ware role for start. What do you think?

</comment><comment><USER2> I work in Scala and Akka everyday at my full time job creating systems to handle video image processing and web client access plus analytics. (I love Scala, warts and all.)

I am also a technical reviewer for Scalatra in Action (Manning) and was a technical reviewer for Developing an Akka Edge (Bleeding Edge).

I'm looking for remote contract work; post a reply if you're interested. I'm pretty easy to find.

</comment><comment><USER3> Maybe a Typesafe consulting partner? I work at SoftwareMill, a Scala software house, we're remote & distributed and quite used to the type of work you need. As for our experience, I think the most relevant bit would be moving yap.tv, a Ruby-based TV startup to Scala.</comment>


Mozilla's JavaScript Battery API  - <AUTHOR> http://www.joezimjs.com/javascript/mozillas-javascript-battery-api/ <comment><USER2> I found docs about that by accident while looking for something else, then made a small test, <http://zash.se/battery.html></comment>


Ask HN: Why block Tor? - <AUTHOR> Didn't we like privacy? <comment><USER2> We changed the title from "Why HN Blocks Tor?" because HN doesn't block Tor. Plenty of users read HN and post to HN using Tor every day, including some in this thread.

There have been issues in the past with Cloudflare and Tor, but I don't know what the current status of that is. Certainly they aren't blocking everybody, or we wouldn't see them.

The one thing HN itself does is moderate comments from brand new accounts that are posted using Tor. We do this because of past abuses by trolls. However, when such comments are good they routinely get restored by users using the 'vouch' feature, or, failing that, we often restore them ourselves. So even this isn't much of a restriction. The main problem is that it makes for a time delay between when a (good) comment gets posted and when other users get to see it.

<comment><USER3> How can HN identify they are behind Tor?

<comment><USER4> You might be interested in this listing from the Tor project:

[https://check.torproject.org/exit- addresses](https://check.torproject.org/exit-addresses)

</comment></comment></comment><comment><USER5> Is it not a cloud flare issue? Hn is behind cloudflare, and cloudflare thinks it's a problem... last time I tried, it's requests verification each time, as do other cloudflare backed sites (my own included)

<comment><USER6> Cloudflare is horrifically hostile to users, triggering blocks and Captchas all the damn time, but I've never once had an issue, or cloudflare interruption, with HN.

It'd give me an interesting conflict if I ever had, as I take Cloudflare captchas as a huge negative indicator of trust.

<comment><USER7> In fairness, Cloudflare's CTO did engage with the Tor folks for quite some time. [https://trac.torproject.org/projects/tor/ticket/18361](https://trac.torproject.org/projects/tor/ticket/18361)

<comment><USER6> Well there was something on Github of a browser addon to mitigate the worst of the challenges, back in summer. I remember it hitting front page here. Seemed hopeful a solution was on the horizon. It seems to have been untouched since, and no sign of an actual solution...

Now the UK is stepping up surveillance silliness I use the VPN rather more, and expect many others are, or will. Which today just means _endless_ unintelligently implemented CAPTCHAs.

<comment><USER8> > It seems to have been untouched since, and no sign of an actual solution...

They're working on it! There will be a talk about this about Real World Crypto 2017 in January, see "Solving the Cloudflare CAPTCHA" on the RWC program: [http://www.realworldcrypto.com/rwc2017/program](http://www.realworldcrypto.com/rwc2017/program).

<comment><USER6> Great. I hope we'll see some movement soon - it's sorely needed. CAPTCHAs seem to trigger more and more often lately.

Shame RWC don't put out videos of past talks - there's a couple of others look pretty interesting too.

</comment></comment></comment></comment></comment></comment><comment><USER9> As a non-logged in lurker, I always get "prove you're legit" when using TBB. I usually hit the "new tor circuit for this site" button a few times until it goes away.

It is really annoying how to even _read_ HN you need to jump through hoops. I can possibly understand commenting and posting being restricted, but there's not reason to block simple views, it's not like anyone is going to use tor for ddos.

<comment><USER10> > it's not like anyone is going to use tor for ddos.

Perhaps not DDOS, but yes, tor is absolutely used maliciously:

> Based on data across the CloudFlare network, 94% of requests that we see > across the Tor network are per se malicious. That doesn't mean they are > visiting controversial content, but instead that they are automated requests > designed to harm our customers. A large percentage of the comment spam, > vulnerability scanning, ad click fraud, content scraping, and login scanning > comes via the Tor network. To give you some sense, based on data from > Project Honey Pot, 18% of global email spam, or approximately 6.5 trillion > unwanted messages per year, begin with an automated bot harvesting email > addresses via the Tor network.

Source: [https://blog.cloudflare.com/the-trouble-with- tor/](https://blog.cloudflare.com/the-trouble-with-tor/)

Counterpoint: [https://blog.torproject.org/blog/trouble- cloudflare](https://blog.torproject.org/blog/trouble-cloudflare)

</comment></comment><comment><USER11> IIRC HN only blocks Tor for recently created accounts, but perhaps HN has a more complex criteria.

For more details, you can try contacting the mods email@example.com (from a throwaway email?)

<comment><USER12> I thought it was a shadow ban on new accounts. You need people to vouch for you to get out, right?

<comment><USER2> Not for most new accounts, no. A small minority of new accounts' comments get autokilled because of past abuse by trolls, and those often get rescued by vouches.

</comment></comment></comment><comment><USER13> I've posted on HN via Tor without issue in the recent past.

</comment><comment><USER14> I block tor from many of my servers. I started noticing that whenever I had an issue with someone attacking a server the source traced back to an exit node. What I found when I looked at the traffic coming from the exit nodes was that the vast majority of it was malicious. There was a massive amount of automated password guessing, exploit attempts, and attempt to connect to botnet controllers/backdoors.

I'm all for anonymity. However, until the tor project puts some effort into outbound traffic filtering for exit nodes it is too much of a time sink and headache not to just blackhole it all on servers that either do not serve public content or where anonymity really isn't needed/justifiable.

I put the code I use to block tor exit nodes in the public domain. You can download it here: [https://github.com/vab/torblock](https://github.com/vab/torblock)

<comment><USER15> There's underblocking and overblocking. Underblocking is allowing TOR traffic through, but also letting TOR traffic flood your servers.

It's obvious that if you have a flood of nefarious traffic like this then you should throttle the TOR traffic. Overblocking is outright blocking TOR with no reason other than because you can, and it leaves many legitimate users frustrated and feeling like the site just self-censored itself.

It would be suitable in these cases to strike a happy medium and allow some TOR traffic through, but throttle suspicious-looking requests like mini 'swarms' of TOR exit IPs hitting the site all at once, which I think HN does, because some TOR idens work, whilst others do not.

</comment></comment><comment><USER7> To avoid vote manipulation and spam I reckon.

<comment><USER16> Who really cares that much about internet points?

<comment><USER17> I use applicants HN score, and comments as hiring factors.

<comment><USER18> That seems like a terrible idea. Unless you're hiring for a user facing role, one's personal opinions should have very little bearing on their hireablility. It's a number that literally means nothing. Hell you can get down voted for having perfectly logical contrary opinions (e.g. any thread involving politics, diversity or Microsoft). This is something you do if you're just are looking for people who think exactly the way you do.

Personally I refuse to provide any company with material that can link back to my personal social sites. It's simply not worth it. I'm not going to sanitize my digital life for the purposes of getting hired. I don't even provide links to my github because people read too much into what is or isn't there trying to parse out some signal that doesn't actually exist.

<comment><USER17> I'm not asking anyone to sanitize. I look for folks who can clearly argue their point.

It's the quality of the debate, not the value of the position

<comment><USER19> It's not though. If you're not arguing a pro-groupthink position you'll get down voted regardless of the quality of your commentary. See just about any politics related thread for an example of that.

Note that only applies to subjective topics. If you're commenting about a purely technical topic then the voting system does seem to work more effectively.

<comment><USER17> To be clear. If one candidate has an HN rank of 10 and another has a rank of 100 it doesn't automatically place them ahead. That would be stupid.

Nor does having downvotes cause any automatic placement adjustment.

I review their comments manually to tell me about the person. It's a qualitative evaluation, not purely quantitative.

<comment><USER19> You previously said:

> I use applicants HN score, and comments as hiring factors.

I get reviewing the comments manually, but what could you use the score for besides a group think filter? Might be great for a sales position ...

</comment></comment></comment></comment></comment></comment></comment></comment><comment><USER20> Posting via Tor now. I had to complete a reCAPTCHA before I was allowed to see the page, but no problems on logging in. Exit relay 22.214.171.124.

I don't usually use Tor but I thought I'd test what OP's saying.

</comment><comment><USER21> It works ok here!

</comment><comment><USER22> 90% of our Tor traffic was script kiddies</comment>


Is it illegal to hack your own Heroku web app? - <AUTHOR>

In the USA or Canada? <comment><USER2> No, you would be an authorized party to attempt to find an exploit in your code. Since Heroku apps are heavily isolated from each other, hacking it doesn't really induce many problems for them (only you).</comment>


Don't make bicyclists more visible. Make drivers stop hitting them - <AUTHOR> http://www.washingtonpost.com/posteverything/wp/2015/04/15/dont-make-bicyclists-more-visible-make-cars-stop-running-them-over/?hpid=z4

<comment><USER2> previous discussion: [https://news.ycombinator.com/item?id=9385951](https://news.ycombinator.com/item?id=9385951)

HN Search: [https://hn.algolia.com](https://hn.algolia.com)

</comment><comment><USER3> This is my exact thought every freaking day while cycling to the office.</comment>


Announcing Lighting w/ actionable, contextual and analytical DB - <AUTHOR> http://www.lightningplatform.com/

<comment><AUTHOR> Would love people's feedback on this. Just launched this less than a few hours ago. The message I am trying to get across may not be clear (feedback appreciated).

In the end, Lightning is a cloud based development platform that offers a different approach to standard databases. Each entry is an object. Like in the real world, an object can be actionable (like "reading" a book), is contextual (like reading the "French" book because I speak French), and has built in analytics for the different actions I perform.

The point is to simplify content creation and analytic creation. But at the same time tying it all to a user account so that the content is better targeted for users, a win for everyone.

</comment><comment><USER2> a better punch line would help, because the description is great. but too long.</comment>


Intel Fourth-Quarter Net Income $2.3 billion, Up 875% - <AUTHOR> http://www.intel.com/pressroom/archive/releases/2010/20100114corp.htm <comment><AUTHOR> How did they do that? Seems that not all big companies are suffering due to global economy.</comment>


The Web Server Benchmarking We Need - <AUTHOR> http://blog.ianbicking.org/2010/03/16/web-server-benchmarking-we-need/ <comment><USER2> Having been subject to benchmark criticisms myself, I say this to all the complainers: walk the walk instead of simply talking the talk.

It is practically impossible for a benchmark to cover all the special cases and what-ifs. If a benchmark doesn't cover the corner case that you want, fix it.

Every benchmark that is too small will be criticized as a not doing anything useful, and every benchmark that is too big will be criticized as too polluted by external factors.

</comment><comment><USER3> While we're thinking outside the bun, let's consider not just the web server, but the whole stack. That includes load balancers, message queues, data stores, etc and of course all the network gear that we often forget about. A fancy web server benchmark isn't going to tell you squat about how all this stuff will perform together for you because there are too many variables.

The closest we can get to useful metrics is looking at large-scale sites that handle high load in the so-called "real-world". These usually take the form of experiential stories which, incidentally, is how most knowledge is shared.

The solution is more stories, not a fancier benchmark.

</comment><comment><USER4> Is there anything to test here? If your process uses unbounded memory, you have to kill it. If your proc uses blocking calls in the event loop, it's going to block the whole process until it's done.

No benchmark or special server is going to prevent your code from being bad. So make your code be not bad.

<comment><USER5> PHP successfully recovers from memory leaks, runaway processes, and any kind of deadlock. App Engine does too. And so does mod_wsgi. It can be done.

<comment><USER4> Yeah, by spawning a new process for every request. Fork is fast, but not forking is faster.

<comment><USER5> All of these keep a pool of worker processes, they don't fork each request. With a non-trivial application the speed difference is negligible, though the memory difference is not.

PHP also has language-level isolation from request to request, but extensions are still long lived and require the process to be killed in some cases when they misbehave.</comment></comment></comment></comment>


Show HN: MapStreetview – point & click streetview - <AUTHOR> http://mapstreetview.com <comment><AUTHOR> Main unique selling points over maps.google.com & others:

1) click to view; no more pegman dragging and dropping

2) splitscreen view of map & streetview</comment>


HN Meetup Munich: April 2012 - <AUTHOR>

The final tally of the vote (http://doodle.com/5durtx5tctbrrmxv) is in and I'm happy to make the announcement:

  Date:       Wed 18 April 2012
  Time:       19:00 -- open-ended
  Location:   Reitmor 3
              Reitmorstrasse 3 (no, really!)
              80538 Munich
              http://www.reitmor3.de
 Agenda:

  1) Organization of future meetups -> regular "HN Stammtisch"?
  2) Optional: speed talks
  3) Mandatory: Pretending to be social, having a good time,... you know the drill
 If you'd like to present something, please give advance notice for planning. There is a beamer but keep in mind that people will be drinking beer while you talk. "Kurz und knackig" is the word => no 50+ slide ppt's please! Cool animations or other demos, OTOH, that'd be more like it.
Last but not least, if you are (part of) a startup, company, or financier, this is an excellent opportunity to step in front of the HN Munich community. To support our meetup with free pizza, beer, or caviar, please leave a comment or contact me at email@example.com.
Happy Easter holidays,
HSO
<comment></comment><comment></comment><comment>--
PS: A few words about the location (long version)
Last time, we met at Couch Club (ht kojote). That location was well received but unfortunately, they want a minimum cover. That means either we take a bet on enough people attending and avg tab sizes, or find some sort of sponsor. Yes, the money isn't much but it does add a layer of overhead.
Therefore, this one time, I take the liberty to override the result from our location poll (http://moreganize.ch/bdLxiKV9lyf) because a) the result is within what I consider margin of error (12:10 in favor of Couch Club) and b) the "participation rate" (22 out of 54) was rather low.
I would have walked the famous extra mile and contacted either potential pizza sponsors or run a super-sophisticated agent-simulation on the university cluster to estimate arrival rates, tab sizes, and likelihood of breaking the min cover from thin air if the results had been stronger. But this looks like most don't care that much between the two choices.
I apologize if someone had strong feelings about it (or democracy). Take solace in the near-certainty that future meetups will happen and that their location is not fixed yet.
</comment><comment>----
TL;DR: I overrode the location poll because the weak margin and participation didn't seem to warrant extra effort. Let's discuss the future at our meetup. </comment><comment><USER2> Thanks a lot for organizing this! We all really do appreciate!

Happy Easter to the MUC HNers!

</comment><comment><USER3> Won't be able to make it this time, but I hope we can have a mailing list setup to catch the updates, and keep in touch till the next meetup ..

</comment><comment><USER4> Great to hear that a new meetings is on the way. Will do my best to attend.</comment>


An open question (rant) about Node.js - <AUTHOR> https://gist.github.com/jamesob/9548827 <comment><USER2> It's worse than that: it's true that in-process asynchronicity is a performance optimization, but it doesn't follow at all that the code needs to _read_ asynchronously.

There is no performance reason to make developers hand-code in the continuation-passing style. That should be the job of a compiler or runtime environment.

Erlang does it. Stackless Python does it. Go does it.

The whole "look how fast Node is due to all my hand-rolled callbacks" is a total fallacy. It's a case of taking perverse pride in an accidental wart of your language.

Slowly the node community is maturing and beginning to realize that if they want to build robust, fault-tolerant software they need better primitives than hand-rolled callbacks. Hence the slowly rising mindshare of Promises, which are strictly better, but would be better still if the language itself baked them in implicitly (consider that nearly any value in a Haskell program is represented by a promise under the hood, but you never have to deal with them explicitly).

<comment><USER3> _There is no performance reason to make developers hand-code in the continuation-passing style._

node certainly recognizes this. However, there is no need to do anything here since JS is taking care of this with ES6. Even if you don't want to use ES6, there are now multiple ways to compile it down to ES5.

 _Hence the slowly rising mindshare of Promises_

Promises are declining, and honestly doesn't really solve the problem in most cases. A couple of years from now, you won't see promises on the server. You should see tjholowaychuk/visionmedia's co[1]; that's how JS is going to look like in future.

In terms of expressiveness, there isn't much to choose between dynamic languages. While it took some getting used to, programming JS wasn't that different from writing Python. However, JS/node is succeeding because this is the first time ever that we have a language/platform that is truly write-once, run-anywhere. A significant advantage in favor of node.

[1] [https://github.com/visionmedia/co](https://github.com/visionmedia/co)

<comment><USER4> > Promises are declining, and honestly doesn't really solve the problem in > most cases. A couple of years from now, you won't see promises on the > server. You should see tjholowaychuk/visionmedia's co[1]; that's how JS is > going to look like in future.

I'm impressed that you apparently completely failed to realise co is an abstractive layer over promises, something it states upfront:

> Generator based flow-control goodness for nodejs (and soon the browser), > using thunks _or promises_

What you yield from your generator is not magical pixie dusts, it's promises.

<comment><USER5> Actually, it's not promises. It's generators.

co thunkifies promises: [https://github.com/visionmedia/co/blob/master/index.js#L209-...](https://github.com/visionmedia/co/blob/master/index.js#L209-L215)

<comment><USER4> > Actually, it's not promises. It's generators.

The generator is the driver, but the driver on its own its useless if it's got nothing to drive.

And what it has to drive is promises (or thunks since most existing node code is thunks-based I guess it makes sense to support that. taskjs uses promises more or less exclusively for the same purpose, Python 3.4's asyncio uses behaving much like JS promises, etc...)

<comment><USER3> Seems you have misunderstood how it works. [https://github.com/visionmedia/co/blob/master/index.js](https://github.com/visionmedia/co/blob/master/index.js)

Typically in apps using 'co', functions will return generators, not promises. Promises were probably added because of all the existing code out there.

Here is a file from my ongoing project, should explain how generators are being used. See getPosts() or addPost() [https://github.com/jeswin/fora/blob/master/server/src/models...](https://github.com/jeswin/fora/blob/master/server/src/models/forum.coffee)

[Edit: Just read your earlier (Gr.GP) comment. Might want to avoid the unnecessary snark, especially when there's a chance you might be wrong.]

<comment><USER4> > Seems you have misunderstood how it works. > [https://github.com/visionmedia/co/blob/master/index.js](https://github.com/visionmedia/co/blob/master/index.js)

No.

> Typically in apps using 'co', functions will return generators, not > promises. Promises were probably added because of all the existing code out > there.

Again, generators in and of themselves don't do anything (for async code). Something needs to be generated/yielded, and that thing is the reification of an asynchronous operation. Such as a promise[0]. Generators and promises fulfil different roles, generators are used as imperative "sugar" for chaining promises.

The generators you yield will ultimately resolve into a series of async operations (promises or "thunks"), yielding a generator is convenience (not very useful convenience either, since `yield*` exists the ability to yield generators just complexifies the driver)

[0] and in the case of co a "thunk"[1], because node

</comment></comment></comment></comment></comment></comment></comment><comment><USER6> _Please stop asking questions like this about Node.js._

The superfluous obtuse complexity that node.js forces is what's keeping ninja programmers like us employed. If we don't keep switching to strange new things every few years, our wages will stagnate as there's less separating our rock- star tech from lower-salaried uncool "enterprise" software.

Being able to manually manage continuations rather than using a language that does it for us is a high barrier that keeps us in demand, the same way that having to manage memory manually keeps people from becoming C programmers. Losing this edge is not something we should encourage.

Just imagine - if all of these "learn to code" programs actually work, the market will be flooded with new programmers. Thankfully, if we make sure that they only learn easy things like Python, we can continue to convince the world that things like Node.js and MongoDB are the future. While the rest of the world is still catching up on basics like Python and Postgres, we'll be one step ahead of them.

People questioning Node.js like this more and more are a sign that we need to find the next hotness and move there if/when node.js sinks. If we can convince people to build more things in the next hotness, then we can get in early before the masses pick up on it, ensuring that we can maintain our spendy lifestyles and free lunches in SF.

(/s)

<comment><USER7> Node is passing the Peak of Inflated Expectations and into the Trough of disillusionment. Rails is firmly in the Plateau of Productivity.

There are some genuinely interesting ideas coming out of node, and I think it's unfair to just call it obtuse complexity. I think that node.js has been a call to return to small, focused modules that will percolate back up.

An attempt to keep things small and focused is nothing new -- Unix started the trend in the 70s. Everything old is new again. I think they're forging some new patterns that take advantages of small, composible blocks through NPM in a way that CPAN and the like were never quite able to exploit.

I think node has an exciting opportunity, coupled with single page apps and browserify, to be a common front end to disparate web services. And, to me, that's exciting.

</comment></comment><comment><USER8> Here's a fun fact that every Amiga programmer seems to understand that Unix programmers just never really got: actual computing hardware is _inherently_ asynchronous and interrupt-driven. The whole Amiga system is structured around using the CPU to program the custom chips and initiate DMA transfers, and installing interrupt handlers that perform the next phase of the program once the custom chips finished their work. It's _how shit got done_ on the Amiga. By contrast, the lack of good support in early Unix for asynchronous I/O is said to be one of the reasons why X11 is such an ugly pile of hacks.

Even on Unix, if you wrote a server procedurally what you'd end up doing is having main() start a select loop that... dispatches incoming events to callbacks. All Node does really is to abstract away the select loop -- one less thing for the developer to worry about. The Node way is _simpler_ than the conventional C way.

So no, there's nothing inherently superior about linear, procedural code, especially when such code is not at all how a computer system that must interact with the outside world works. Whether asynchronous, callback-driven code is confusing or not depends on what background you come from. Put an old- school scener in front of Node and he might form an entirely different opinion about it from OP.

<comment><USER9> I think the question the article is asking is "Why did did Node stop at abstracting the event loop away, when it could've also abstracted the execution context?" There are languages that do this - Go, Erlang, Haskell, and Python 3.4 all provide a sequential process/coroutine abstraction on top of an implementation that's fundamentally asynchronous.

The question the author has isn't about implementation, it's about programming model. Programming languages & frameworks are ultimately made for humans, and most humans find it easier to reason about "Do this thing, then this thing, then these other things, until you're finished" than "I'm doing all these things at once!" We have computers to organize all the events, stack frames, wait queues, etc. that take us from the machine to the editor window.

<comment><USER8> What I'm saying is that a programming model that's confusing to you is perfectly natural to someone else who comes from an environment where that model is all you have. People have used interrupt/event handlers and callbacks _literally since the dawn of computing_. It's nothing new. It just has to be learned, the way programming itself has to be learned.

<comment><USER10> Have you considered that the reason the dumb, synchronous Unix model is much more common is because it's easier for people to understand? There must be some reason that it succeeded despite being less efficient.

("The Rise of Worse Is Better": [http://dreamsongs.com/RiseOfWorseIsBetter.html](http://dreamsongs.com/RiseOfWorseIsBetter.html))

</comment></comment></comment></comment><comment><USER11> I understand that Node.js is really popular here and that my opinion may be an unpopular one. Maybe this will change, but I cannot find a use case for Node where I wouldn't choose something else.

If I want to prototype (performance is not a constraint), I'd use Ruby (or Python) before Node. If I need concurrency or if performance is a constraint, I'd choose Go (or Clojure, or Scala, or Erlang) before Node. I understand the argument about using JavaScript throughout the stack, but I see this as specious at best. Front-end and back-end development require different domain knowledge, not just language proficiency.

Perhaps using Node opens a project to a larger community of developers. But under this argument, using C or Java also seems a good choice.

<comment><USER12> Here's an example: [http://clara.io](http://clara.io)

Obviously this a fairly heavy client application. But imports, exports and renders are server side operations. They manipulate the scene graph using the same code that the client side uses to manipulate the scene graph.

Live render is a great example. When you start a live render, we start a client on the server, but instead of translating the scene graph into WebGL, it translates the scene graph into V-Ray. Any changes you make to the scene are sent to the server-side client using the same mechanism used to synchronize changes between 2 users editing the same scene.

If we used a different language in the front end and the back end, we would have ended up with a _lot_ of duplication of effort.

<comment><USER13> Very cool!

I started working on a winged-edge WebGL editor--really happy to see other people getting this stuff to work!

<comment><USER12> Neat-o. We use a more convention polymesh representation. Email is in profile if you want to collaborate or interoperate.

</comment></comment></comment></comment><comment><USER14> There's no sane possibility of a hybrid for Node.js. The concurrency model in JavaScript is built for one thread [that runs JavaScript]. For servers, in such a model, _everything_ has to be async. A 20ms synchronous operation means 20ms of time that you cannot handle new connections or process others that have sent or received data. It's critical to the success of the platform that almost everyone insist on async-only code. Recall that the asynchronous model has been done before by Twisted and EventMachine, but they have exactly this problem: there's tons of Python and Ruby code out there that's synchronous and if you accidentally use it (even by using a dependency of a dependency), the server falls over. In Node, it's very hard to accidentally use it, by design: you have to write an add-on or use one of the few *Sync functions, which are well-known as red flags in a server.

In terms of why this model is compelling: it scales well, and it's a relatively simple model of synchronization. The 5-line HTTP server on nodejs.org scales to a very large number of requests per second. Having spent a lot of time in the heavily multi-threaded C world (and loving it, btw), it's much harder to shoot yourself in the foot with JS and this model, and the failure modes are usually less severe. (Many C programs use this model as well, including haproxy and much of the kernel.)

It's not all win. Blocked operations are harder to debug. But you can work around things like that, and overall it's a nice environment. And FWIW, once you're familiar with it, the waterfall pattern quickly becomes as second- nature as writing synchronous code. (More complex ones are still tricky, but the basics do become second-nature.)

[edit: I meant that there's no sane possibility of a hybrid between synchronous and asynchronous semantics. That doesn't mean you couldn't use a language feature to make async code look synchronous. That's a much more subjective discussion, but I prefer explicitness over compiler magic.]

<comment><USER2> This is precisely the fallacy I'm talking about in my top-level comment.

You're conflating asynchronous _implementation_ (which I agree is good), with structuring the code in continuation-passing style (which is bad, for a lot of reasons).

It's all just plumbing, and people demonstrably do it badly if we judge by how well the typical node module deals with unexpected failures. The official node policy on exceptions is that you should let the process die. Not because it's impossible to write exception-safe code, but because almost nobody does it, because it's too hard to do when you're plumbing it all by hand.

<comment><USER15> Do you have a reference for the "official policy" statement? Any experienced, systems developer, regardless of language, would never think that was a good thing.

In addition, it is not hard to handle the errors due to callbacks. The Node variant of Javascript is basically dynamically typed C and error handling actually follows a very similar pattern to what is standard practice in C. Fault-tolerant systems are hard, period. But since the most important fault- tolerant systems (cars, planes, etc) are all written in C. I do not see why it is "too hard" to build a fault-tolerant server with Node since error handling follows a similar pattern.

Just to be clear, I am not advocating the use of Javascript for automobile software.

<comment><USER16> > Any experienced, systems developer, regardless of language, would never > think that was a good thing.

I don't know about node, but it's certainly the Erlang way of doing things. You let the process die and leave a supervisor to deal with the problem.

<comment><USER17> With the crucial difference that in Erlang, the thing that dies is extremely fine-grained, while in Node it's the entire server. In other words, this is far from the Erlang way of doing things.

</comment></comment></comment></comment></comment><comment><USER18> I can't speak to node (don't use it), but in Erlang, I find async far easier to reason about than sync for the simple reason that I don't have to plan for what will happen if some operation over the network breaks _during_ a request.

In my head, there's no difference between async and sync. A synchronous call is just an async message + and async acknowledgement of receipt. If I write it that way, there's a lot of failure modes that I don't have to think about.

Also, in my head, concurrent code looks like a sparse group of people spread over a large, grassy, hilly plot of land shouting at each other from differing heights and many yards distance over the whine of shifting winds. I find it easier to reason about the shouts alone, instead of shouts + their responses, because a response is nothing but another shout.

</comment><comment><USER19> I have a hard time 'listening' to these rants about how hard it is to reason about asynchronous code. It's NOT hard, it's DIFFERENT.

This isn't some new Node-Sauce. The idea of an event-loop asychronous programming domain has been around forever in various forms, first in games and desktop GUI programming and the way one reasons about it hasn't changed, just because it has been applied to a new class of problems.

I suspect that there are two things going on here:

1\. A LOT of people dislike Javascript. As a result, they're starting with that negative point of view and then finding reasons to support it.

This is not unlike people who argue about Python white-space. They're really saying "I don't like Python." The white-space issue is incidental.

2\. Lack of experience / exposure

I suspect that folks who have trouble with this, frankly just aren't experienced enough. I'm not saying they aren't skilled, just not experienced in this realm.

Once upon a time, these same sorts of 'arguments' were used against Ruby ( ala the Rails explosion ). Nothing much has changed about Ruby, other than it has managed to be around long enough to influence a new generation of programmers.

<comment><USER20> Yes, it is harder. You are attempting to reason about code in the absence of a useful stack trace. You're essentially taking your structured programming language and throwing it away and returning to the spaghetti-code era of code flow (this time with islands of structure). Both automated and human analysis is legitimately harder under these circumstances.

"2\. Lack of experience / exposure"

Quite the contrary. I've had abundant experiences with both event-based models (which _hardly_ started with Node!) in several paradigms (GUI, server, network) and models like Erlang, Go, and Haskell. It is _no contest_. The latter is sane. The former is not. If you have the opportunity to choose freely, and you choose the event-based model, you have chosen poorly.

Believe me, it is not the _critics_ of Node who are the inexperienced ones. All the evidence I've seen points _strongly_ in the opposite direction. Node advocates could start by getting their understanding of how their competition works out of the mid-1990s.

Ironically, in the end Node will eventually work their way around to working like these models. The signs are all there, the parallel evolution is clear, and apparently the process has already begun with Meteor. I wonder if the community will ever acknowledge how crazy they've been as they careen from one "solution" to the next at breakneck speed, all the while crowing about how much better their stuff is, even though what they consider "their stuff" can't seem to stay stable for 3 months?

<comment><USER19> "Quite the contrary. I've had abundant experiences with both event-based models (which hardly started with Node!) in several paradigms (GUI, server, network) and models like Erlang, Go, and Haskell. It is no contest. The latter is sane. The former is not. If you have the opportunity to choose freely, and you choose the event-based model, you have chosen poorly."

Nonsense. First, if you read what I said, I explicitly stated that this IS NOT new and has been around forever in many different forms, even citing some of the same domains you did ( GUI ).

Second, everything you state here can be summed up with "I don't like it." Your preference for the models in Erlang, Go and Haskell are opinions, nothing more. I agree with you that there are some interesting approaches to different event-systems, across many different styles and languages, but there is nothing inherently superior to one over the other, beyond "taste".

"Believe me, it is not the critics of Node who are the inexperienced ones. All the evidence I've seen points strongly in the opposite direction. Node advocates could start by getting their understanding of how their competition works out of the mid-1990s."

I don't believe you, because I've been programming since the late 80s/early 90s, and I think Node is a fine choice for building certain types of software. I've been around the block more than once myself, through a ridiculously long list of programming "shifts".

The things you're espousing about Erlang, Go and Haskell's superiority were the same things that C/C++ programmers were saying about Java in '95.

"Ironically, in the end Node will eventually work their way around to working like these models. The signs are all there, the parallel evolution is clear, and apparently the process has already begun with Meteor. I wonder if the community will ever acknowledge how crazy they've been as they careen from one "solution" to the next at breakneck speed, all the while crowing about how much better their stuff is, even though what they consider "their stuff" can't seem to stay stable for 3 months?"

I don't know where this 'community' is, but frankly, most communities have advocates that talk too loud about things and make ridiculous claims about things. I believe it was Scott McNealy who said "I don't know what your question is, but the answer is Java". These things have nothing to do with actually writing code and creating good/usable software. I submit that you should ignore these people and focus on working with tools that help you write software.

If Node does not help enhance your ability to create software, it's not the tool for you. That doesn't mean that Node is inferior or stupid or useless, it just means it's not the right tool for YOU to use.

</comment></comment></comment><comment><USER5> Async code is super easy to reason about when using ES6 Generators, which are available now in node v0.11.

Here's both a series and a waterfall example: [https://github.com/visionmedia/co#example](https://github.com/visionmedia/co#example)

</comment><comment><USER21> Having written nodejs for about an year I would never choose it again if I have given the choice.

* No Error handling. The process has to be restarted if something goes wrong.

* No thread local storage equivalent. As a result you can't do simple thing like differentiate log originated from different http requests.

* Callback makes the code unreadable

* Javascript has no real method or in other words 'this' is not attached to a function and is determined by way the function is called. So you have to wrap most of the callbacks with _.bind()

* Mixing callback, promise and event emitter api introduce lot of boilerplate code.

* No stacktrace. Debugging error is PITA

* No preemptive scheduling. So you have to be extra careful that you never spend too much CPU time.

* No easy way to handle back pressure.

<comment><USER22> These are exactly the reasons I have stayed away from it until now. I don't understand this one though, what does it mean?

* No easy way to handle back pressure.

<comment><USER9> I did some Googling. "back pressure" in the Node context is basically what most people refer to as "flow control" or "overload behavior". Node programs work by enqueueing a bunch of work items for the event loop to handle, and then going on and doing a bunch more stuff until all the work items have been handled and their callbacks have been called. In the process, those callbacks may themselves enqueue further work items.

The problem is that all these callbacks themselves take up memory and hold onto references in the V8 heap. And so if callbacks are being enqueued faster than they can be processed, the heap will get more full. V8's GC takes time proportional to the amount of live data, and so GCs will become more frequent and take longer. This further reduces the amount of available CPU time to process callbacks, which increases the amount of live data, until the system thrashes to a halt.

Haskell programmers (who use a similar scheduling mechanism for lazy evaluation) call this a "space leak", and it's a big problem in Haskell as well. The solution is for the runtime to let the calling code know that it's overloaded, and then either temporarily block the calling code from creating more callbacks until existing ones have run, or let the calling code gracefully degrade and choose to handle things in a simpler fashion.

<comment><USER22> I see, the tomcat tcp connection backlog is a similar concept I guess, just thats it blocks at some point once the queue is full, unlike node.js?

</comment></comment></comment></comment><comment><USER23> The gist reads like someone looking for an excuse to give up and move on. Every time I run across someone bitching about Node's programming model I'm reminded of "The Blub Paradox".

[http://paulgraham.com/avg.html](http://paulgraham.com/avg.html)

I have to admit that learning to think in callbacks was _initially_ an exercise in frustration. But it wasn't _that_ hard.

Most of the pyramid-type issues can be resolved by applying principles that are also valid in synchronous environments (like modularity).

If modules and named functions aren't enough, you can subclass EventEmitter to further decouple. For more complex cases there's [https://github.com/caolan/async](https://github.com/caolan/async), which makes many nontrivial async problems relatively easy.

Why is it so hard for so many people to appreciate Node for its merits? The code isn't going to execute in a perfectly linear way, so why should it have to be written as if it were?

</comment><comment><USER24> Asynchronicity is great, but javascript doesn't provide the tools to do it properly.

> (...) try writing a function call that requires information from two > separate HTTP API responses; I basically need to draw a diagram of what > happens with async.waterfall for a task that, given synchronicity, would've > been solved with a trivial three-liner.

It's a lot easier to handle asynchronicity in strongly-typed languages with monadic for comprehensions. In Scala, for example:

        val res: Future[Foobar] = for {
          a <- makeHttpRequestA()
          b <- makeHttpRequestBFromA(a)
        } yield new Foobar(a, b)

You can then register callback functions which operate on either a Success[Foobar] or a Failure[Throwable].

Synchronous code isn't simpler, it just hides complexity like caltrops in tall grass.

(slightly modified version of my post on Github)

<comment><USER25>
        co(function *(){
          let promiseA = makeHttpRequestA()
          let promiseB = makeHttpRequestBFromA(yield promiseA)
          var res = new Foobar(yield promiseA, yield promiseB);
        })();

<comment><USER26> with scala.async [1]:

       val res = async {
         val a = await(makeHttpRequestA())
         val b = await(makeHttpRequestBFromA(a))
         new Foobar(a, b)
       }

[1] [https://github.com/scala/async](https://github.com/scala/async)

</comment></comment></comment><comment><USER27> I was reading somewhere (maybe Hackernews itself?) that synchronous programming and/or blocking are actually features and not disadvantages. After working on a huge Node.JS project, I regret taking on anyway (I had to do it for the moolah, aka money). I am now convinced that migrating from Rails to Express (A node.Js framework) was a total mistake. Please note, I do not mean to advertise rails in particular, infact, you can replace rails with anything else thats fits the analogy - perhaps maybe a python framework.

However, this mistake has taught me something(s) very valuable -

1) Don't trade in a tried and tested framework for something like Node.js, that will open up a different level of problems altogether.

2) No matter what language you use, you WILL face issues at scale. Be it Node.js/Scalatra/Rails. You might as well face it with a tried and tested framework.

3) Don't re-write your own framework in something like Go or Haskell or C or C++ or etc. thinking it will be scalable, etc.

You will -

a) Waste massive amounts of time re-inventing the wheel,

b) Open yourself to possibly dangerous security vulnerabilities (Eg: Handling encrypted cookies),

c) Waste your time on things that don't really matter instead of on something that actually matters - The product itself.

4) Anything that you can scale by throwing more money at it is for the win[1]

Why am I talking about scalability, when the discussion is about Async. vs Sync. programming? Because of the reasons many people cite using Node.Js and the like. It's just not worth it. Focus on the product first and the architecture later. This comes from someone who wasted a year of his life doing everything above.

[1][http://highscalability.com/blog/2013/4/15/scaling- pinterest-...](http://highscalability.com/blog/2013/4/15/scaling-pinterest- from-0-to-10s-of-billions-of-page-views-a.html)

<comment><USER28> Sorry you had a bad experience, bud. I am on a team of six, all pretty experienced with Node, and we don't have those issues. We've been going production on it for about a year and a half now. People cried a little in the beginning because it was different but now that we have embraced (and love) callbacks, we rip through development pretty quick. Things aren't always done for you but it's getting pretty rare at this point.

</comment></comment><comment><USER13> So, here's how I think about it:

Any program, of any type, has a synchronous story to it: data came in, then this happened to it, then this happened to it, then it left. If you follow the program execution, this is how it works.

It's very easy to reason about, right? And it's very easy to program this way.

One of the biggest breakthroughs in computer engineering was the idea of multiple concurrent processes (and nevermind that behind the scenes they were still running on a single processor!), and then later still true concurrent multi-processor architectures.

We developed operating systems and compilers and runtimes _specifically_ to let us take advantage of that without the having to break from easy-to-reason- about synchronous code.

~

Node said, hell with it, everything is async all the time. This is really annoying at first, because there are many many times where sync is the best way to reason about something.

That said, there is some beauty in such a hard-line approach: if you are truly async, handling an error is the same as handling a task which takes too long. Once you've paid that price properly, you're fine. (This is also what makes Erlang kind of cool.)

The big frustration is that there really ought to be a way of writing synchronous-looking Javascript code (ES5) which--behind the scenes!--is multiplexed and made to run concurrently and without blocking. The mechanisms are all there for programmers to deal with exceptional failures (exceptions, anyone?), and so are synchronization points (functions having multiple arguments should probably join on the evaluation of those arguments before being invoked--that's something the interpreter should be doing already).

I guess what I'm saying is...why don't JS interpreters do lazy evaluation and late binding?

</comment><comment><USER29> Um, am I the only one who considers Node.js a beautiful tool suited for developing servers?

Why async by default? Because defaults should encourage best practices. It can handle C10K problem out of the box. With libraries like Q, the code can actually be rather easy to follow and maintain.

One of the biggest reasons why a multi-user server should be coded in async- by-default style is as follows: often, you want to obtain objects and not care about how they are obtained. In procedural languages, this isn't possible. Consider the following challenges:

1) Query sent to 10 different shards, wait until they all return, combine the results in the app, and then use them. In a synchronous language, you HAVE TO go to one shard, then another, then another, and the time is 10x longer. In an async style, it's only as long as the longest query!

2) Some of the objects may have already been cached, whereas others need to be obtained. In addition, the ones that need to be obtained shouldn't be requested more than once at the same time, but callbacks should be placed on a waiting list.

3) Various parts of your code (e.g. from different requests) might want to request the same object. Not only that, but you might want to batch your requests. That is to say, wait 10 milliseconds and see if any other (unrelated) parts of your code also want to grab some objects from the same DB table. Suddenly you are issuing a lot less queries because you can build middleware like this easily in JS. Not so in PHP for example - I would know. A lot is wasted there.

4) Let's consider #1 some more. The queries may go to other HTTP endpoints - do you really want to wait that long for I/O? Furthermore, even if they are going to local MySQL shards, your site becomes slower with an extra factor of O(log n) with the number of users. Kind of like when you use a relational database instead of a graph database for one-degree-away lookups. It could have been O(1) but instead you introduced another factor of O(log n). Not terrible, but certainly gives you a 5-20x cost down the line.

<comment><USER30> > In a synchronous language, you HAVE TO go to one shard, then another, then > another, and the time is 10x longer.

Have you ever heard of threads?

<comment><USER31> Threads are more difficult than writing event-loop based code IMO

<comment><USER29> Not just more difficult. Threads are really wasteful to spawn just so you can hit 10 shards. Even if you have a thread pool. Evented I/O is much better than threads.

When NGiNX launched, it was the evented web server in a sea of threaded servers. Guess which paradigm won.

<comment><USER30> It's not a dichotomy. The answer is that both models are useful, and most systems support hybrid threaded and evented modes. Node.js only supports one model, which is a huge drawback.

</comment></comment></comment></comment></comment><comment><USER32> It seems that OP thinks the asynchronous nature of JavaScript was meant as a performance optimization. Let's go back to its origins. It was originally a scripting language for the browser. If it were to be synchronous and/or allow blocking things, such as `sleep()` or whatever else, it would have created an unusable web experience. Web pages would be constantly locking up. It's only recently that it's moved to the server where the asynchronicity on I/O isn't so mandatory.

I personally still think it's a good thing, but that could definitely be debated on the server side. Unfortunately, if you want to have the advantage of writing one code base that can run on the browser or server, then it must work the same in both places.

</comment><comment><USER33> I posted this as a comment on the gist:

It's not about performance, it's about <del>parallelism</del> concurrency.

If you don't care for Node's ability to handle multiple requests <del>in parallel</del> concurrently, with a single process with only one thread, than you might not have a need for Node at all. Why not use Ruby or Python?

If you really want to force Node into being a dumb scripting engine, you could use the various sync functions that Node.js provides. Problem is, they block the entire thread. But if you want to, you can. And I believe with some clever hacks, you can use the blocking nature of (for example) the fs sync functions to make other calls (including http requests) synchronous as well. But unless you use Node to build something that's not a server (people build command line tools with it as well), I don't think you want to.

By using synchronous functions, everything Node is built for will go down the drain. Everything done will be done in sequence, and during the time the sequence of operations has not finished, the Node runtime will be unavailable to serve any requests.

I suggest you look into promises as a somewhat saner way to deal with asynchronicity than callbacks, although you can pretty far with just the Async library. The future might be in Generators, a new feature of EcmaScript 6. This is available in V8. You can use this in Node 0.11 (unstable) if you use the --harmony flag. See here for a nice writeup: [http://blogs.atlassian.com/2013/11/harmony-generators-and- pr...](http://blogs.atlassian.com/2013/11/harmony-generators-and-promises-for- node-js-async-fun-and-profit/)

<comment><USER30> You're confusing concurrency and parallelism. Node is a single-threaded event loop. It can only do one thing at a time. Node can perform many operations concurrently, but they're not running in parallel. If any one operation blocks, the whole event loop stalls.

Parallel execution requires multiple threads running on multiple CPU cores. Node requires multiple VMs to do this.

<comment><USER33> Thanks for explaining difference between concurrency and parallelism so clearly! I'll remember this.

I of course realize that Node doesn't truly handle things in parallel. It's a single thread, after all.

</comment></comment></comment><comment><USER34> [https://www.youtube.com/watch?v=bzkRVzciAZg](https://www.youtube.com/watch?v=bzkRVzciAZg)

</comment><comment><USER35> > If asynchronous code is harder to reason about, why would we elect to live > in a world where it is the default?

Because if you do not make it the default, then most libraries you use will make the choice for you by going sync.

</comment><comment><USER36> My big beef with Node.js: it could have avoided the sync/async split entirely by using a synchronous facade atop an asynchronous runtime...but it didn't. Why would you create an entire server platform and _not_ use the chance to simplify the most common use case: fetching data from a database?

I see as a great opportunity completely squandered so that it would cater to the masses: "you don't have to learn anything new!"

Sometimes I think web tech _likes_ being as shoddy and thrown-together as possible.

</comment><comment><USER37> You can have an async implementation and synchronous code. [http://meteor.com](http://meteor.com) does that part quite nicely. The compiler or runtime should be handling this kind of cruft for us in most cases. Just because using Brainfuck might result in theoretically faster code than Ruby doesn't mean we should be writing our server in Brainfuck.

</comment><comment><USER38> >but I'd be very curious to hear a defense of "async-first" thinking for problems that are typically solved on the server-side

Because if your web server blocks every time it accesses a file, or talks to a database, or communicates over the network, then it won't take many requests at the same time before it's completely bogged down.

</comment><comment><USER39> Asynchronicity isn't an optimization, it's a simplification. Blocking code and threads are hard to reason about, but callbacks are simple. You still have to deal with errors in a synchronous world, and an evented system allows you to deal with branching event possibilities sanely.

<comment><USER2> Yes, _pre-emptive_ blocking threads are hard to reason about.

But _cooperative_ blocking threads are just as easy to reason about as node's callbacks, while being significantly easier to structure and make fault- tolerant.

</comment></comment><comment><USER40> My argument would be that if you go 'sync first, async if you feel like it', everyone will just do sync, because it _is_ easier conceptually (or atleast more familiar to a lot of people).

</comment><comment><USER41> In browser content may need to be asynchronous, but HTTP is request/response - nothing asynchronous. Half the web (or more) is not about feeding the browser. Use asynchronicity where needed.

</comment><comment><USER42> Am I the only dev in the world that loves async?

</comment><comment><USER43> As a designer I have been trying to dig this stuff. Server side JS seems like a huge win for all kinds of small websites and interesting ui experiments, because one can stay in one language and avoid code duplication.

Yet in trying to set up some experiments that use a server-side git integration, I bumped into the kind of code described in this thread, and I find it super difficult to understand the async way of doing things.

Here's an example from the homepage of nodegit, which seem to be among the best maintained libraries that wrap libgit2. Thus I imagine it represents idiomatic node.js What the code does, is console.log details of all the repositories commits, git log style.

      // Load in the module.
      var git = require('nodegit'),
        async = require('async');

      // Open the repository in the current directory.
      git.repo('.git', function(error, repository) {
        if (error) throw error;

        // Use the master branch (a branch is the HEAD commit)
        repository.branch('master', function(error, branch) {
          if (error) throw error;

          // History returns an event, and begins walking the history
          var history = branch.history();

          // History emits 'commit' event for each commit in the branch's history
          history.on('commit', function(error, commit) {
            // Print out `git log` emulation.
              async.series([
                  function(callback) {
                      commit.sha(callback);
                  },
                  function(callback) {
                      commit.date(callback);
                  },
                  function(callback) {
                      commit.author(function(error, author) {
                          author.name(callback);
                      });
                  },
                  function(callback) {
                      commit.author(function(error, author) {
                          author.email(callback);
                      });
                  },
                  function(callback) {
                      commit.message(callback);
                  }
              ], function printCommit(error, results) {
                  if (error) throw error;
                  console.log('SHA ' + results[0]);
                  console.log(results[1] * 1000);
                  console.log(results[2] + ' <' + results[3] + '>');
                  console.log(results[4]);
              });
          });
        });
      });

It looks so alien to me! Instead of the tree-walking and looping constructs I know from synchronous languages, we deal with a history that returns events as it walks the tree (btw, why is it that we can launch the history before we attach the event?). Then we have to actually import the 'async' module, because, uhm, we want to specify an order by which the different parts of the message gets logged.

How then the actual callbacks work, I'd be happy if someone can explain that to me—why do I pass a function to an attribute of the object? Why don't I just return the value?

Because the code is not written in a lineair way, I have to keep looking back and forth between callbacks to get a mental picture of what it does.

For the record, what this translates to as synchronous Python:

      from pygit2 import Repository, GIT_SORT_TOPOLOGICAL
      repo = Repository('.git')

      for commit in repo.walk(repo.head.target, GIT_SORT_TOPOLOGICAL):
           print 'SHA %s' % commit.hex
           print commit.commit_time * 1000
           print '%s <%s>' % (commit.author.name, commit.author.email)
           print commit.message

I imagine that, as some of the commenters note, I will be able to start reading the async style if I get enough exposure to it. I wonder though, if it does not make the barrier to entry for server-side JavaScript too high for non-full-time programmers.

In that sense it is interesting that the Meteor team chose to use the package Fibers, that allows one to write node.js in a procedural style. I imagine something like that is necessary for server side JS to become really mainstream in web design…

</comment><comment><USER44> The creator of Node.js said it best himself, that his afraid people associate Node.js with scalability out of the box due to the single threaded asynchronous nature, that there exists something magical about it that would suddenly make your app scalable because it's riddled with async code.

I now use Flask and Python instead of Node.js and it's a total pain reliever. Not having to deal with Javascript is a win on the server side (JS on client side is enough) and the python stack is good enough to handle every bit of Node.js

Truth is, Node.js is in a bubble. I find it that even people who have never programmed before insisting that their new website be written in Node.js.... It reminded me of the dot com bubble, when your grandma would explain to you her rationale for buying dot com stocks (they had no idea).

</comment><comment><USER45> node.js does not impose it for everything. Every command in the fs library that has a natural synchronous interpretation (like reading a file, writing a file, ...) has a sync equivalent (readFileSync, writeFileSync, ...)

<comment><USER46> Try to do that on a production server that has to handle 1000 requests readFileSyncing at the same time ...

sync apis are just a conveniance for console applications, they are not meant for a web server.

So yes node.js DOES impose async programming for everything or node doesnt scale.

<comment><USER47> Sync is great for initialization. Due to the fact you only start up once, you can be as reckless with your performance hindering practices as much as you want as it will never amount to anything more than a few hundred extra milliseconds of startup time.

<comment><USER48> > Sync is great for initialization.

This is an interesting point. I've found in practice that the speed of server startup times depends on the scale of operations. For example, at a small scale, a 600 ms vs. a 1.2 sec startup makes little difference.

However, at scale such differences can matter. More so when something is going wrong that's causing erratic restarts.</comment></comment></comment></comment>


Show HN: React Native FoldView - <AUTHOR> https://github.com/jmurzy/react-native-foldview <comment><USER2> Is there a workaround for Android?</comment>


Ask HN: I wrote a recursive story. Want to know if my thinking is correct. - <AUTHOR> Disclaimer this might be a bit too long so sorry if doesn't add any value to you.
Yesterday I wrote a very short story which goes like this: We are in a recurring simulation. Each simulation is actually a speeding up to find the perfect computer. As we approach to solve the problem, the world resets and starts again to find the perfect computer. As we approach to solve the problem, the world resets and the perfect computer...
I thought this seems to be recursive but it's just that the same sentence repeats to give that illusion. So I thought I  should write a bigger story and see what happens. And so I wrote this: Details that can be added into the above story.
So the higher entity has found that humans cannot be kept in a vegetative state and the matter cost of frequently replacing us is very high. To optimise this we have come up with a software that runs in the brain. The diary is a detailing of this software. We run a simulation where the brain starts from the stone age or some even more primitive state. So that when they have full function of the brain but the only thing they lack is knowledge. With each turn of his life and he's progressing through an increase in knowledge in the human mind. As human's start to grow in number the collective knowledge increases and the time to the acquire more knowledge keeps of decreasing. Then we reach the point where they are not able to know the their birth but at that very moment the software's platform computer resets or has a segmentation fault. This the maximum strength computer the machines have built till now. The machines are constrained by resources and not able to develop better computers. They have timed the resource utilisation of running the farms with this software to the end of the earth. So that resources end at the precise time the sun dies. continues in comments... <comment><AUTHOR> Mechanic of the Simulation: The machines found that the simulation cannot be run continuously. So they let the humans body rest in relation to their body age. This comes from the idea that humans start at the start of stone age where there are less humans and others would be in some form of coma. As the story progresses in the simulation the humans are woken up into the dream. So according to this description the young ones are woken up earlier and older ones are woken up later.

This is done in a farm. The oldest one is the last to die. This way whole farm can run 3 - 4 times before the people gain conscience. Farms die at rate which the machines can create and start new farms. When the earth is about to die, there will be only on farm left and no more resource to create more.

This simulation diary can be written over and over again by exploiting a very slightly illogical statement that doesn't seem so and keep on writing infinitely. I think this is like prime series from mathematics. I don't know how much this can grow or does it even have an end. I don't know if my analogy is correct. I'm pretty sure this a infinitely recursive story of thread of word where I can start at any point and say that this is a wrong statement and start again by building about a new story around it. Like if this story is a function applied on itself and we spit out more words and keep the story going with another bullshit term.

Is the way I wrote the story wrong?</comment>


Angular is unable to build large applications - <AUTHOR> https://github.com/angular/angular-cli/issues/5618 <comment><USER2> Does anyone know if this issue appears with React?</comment>


Google Translator Toolkit is shutting down - <AUTHOR> https://support.google.com/translatortoolkit/answer/9462068?hl=en <comment><USER2> This is the natural progression of a culture that values the new toy over maintenance. It shows in the lack of support and inability to find any human support at all. If your entire success is the next thing or the big swing, there is really no incentive to maintain anything unless its absolutely necessary.

I love the fact that its so easy in computer work, compared to other professions that have to deal with actual physical things, to build whole new things. On the other hand, I hate the attitude it brings to maintenance and customer service. We are the profession of throw-away cities.

<comment><USER3> You know you would think they would at least slow down on sunsetting services as they're releasing Stadia to a chorus of "How long until they kill this?"

</comment></comment><comment><USER4> I think many people overestimate how large the teams who are behind most of Google's products. You have a few large ones like search, ads or cloud, but many of the smaller ones are just a few dozens who over the years dwindle down to just a handful as people realize it is a dead end project. At some point the team is not longer big enough to maintain the project and it gets shut down.

<comment><USER5> Sounds like a management/culture issue. That's not an excuse to not cycle in new staff and people move around.

<comment><USER4> Managers are just employees as well who also pick projects that will benefit their careers. Why work on a small thing with a few million users where you will never get promoted since you are already at the top, when you could just move to the next growth ship where lots of people are hired so moving up the ranks as a manager happens naturally? To me it seems like this culture seems to go all the way to the top. It is not like Sundar got promoted so quickly due to his work on an obscure product.

<comment><USER6> > Why work on a small thing with a few million users where you will never get > promoted

That's the management problem right there. You _should_ be able to get promotions for maintaining/improving a small product. The fact that you can't is what causes this endless shutdown mentality.

</comment></comment></comment></comment><comment><USER7> Posted 2 months ago:

[https://news.ycombinator.com/item?id=21026456](https://news.ycombinator.com/item?id=21026456)

</comment><comment><USER8> This is sort of misleading since it's really just them deprecating things to organize and keep things tidy. You still have access to everything through Google Translate.

<comment><AUTHOR> Does Translate offer the Toolkit functionality? Just checked, but couldn't find anything beyond the usual text or document translation box.

<comment><USER9> I believe google translate offers pretty much all of the toolkit functionality (and more), but packaged in a different way. Take a look at the google translate advanced docs:

[https://cloud.google.com/translate/docs/advanced/glossary](https://cloud.google.com/translate/docs/advanced/glossary)

</comment></comment></comment><comment><USER10> Not much of a news item. They are not shutting down google translate.

</comment><comment><USER11> This horse has been beaten to death, repeatedly now.

Trust that Google's "don't be evil" was earnest has evaporated. *

Belief that product/services from Google are anything more then ephemeral has gone up in smoke.

Promises that hardware would be supported and provide a baseline for the future are nothing more then delusional and wispy.

This is what we get when we have faith in a cloud.

 __edited and updated. Thanks for the correction.

<comment><USER12> If you're gonna beat a dead horse at least get the motto right. It was "Don't be evil" not "Do no evil"

<comment><USER13> > It was

It is still there:

"And remember… don't be evil, and if you see something that you think isn't right – speak up!"

[https://abc.xyz/investor/other/google-code-of- conduct/](https://abc.xyz/investor/other/google-code-of-conduct/)</comment></comment></comment>


FAA considers enhanced inspections after SW Boeing 737 in-flight skin rupture - <AUTHOR> https://www.wsj.com/articles/faa-considers-ordering-enhanced-inspections-after-southwest-air-jet-suffers-structural-problem-11584130012 <comment><AUTHOR> [https://archive.is/v8Cpr](https://archive.is/v8Cpr)</comment>


Anatomy of an Exploit: An In-depth Look at the Rails YAML Vulnerability - <AUTHOR> http://rubysource.com/anatomy-of-an-exploit-an-in-depth-look-at-the-rails-yaml-vulnerability/# <comment><USER2> Ok, at risk of turning this post into a flame war, I feel I'm obligated to call out what it is:

        Ruby devs: stop making excuses!

Security is hard, but there are also a lot of very simple and effective ways to minimize vulnerabilities, e.g. a mere excising of your cerebral cortex for a few minutes before you make a decision is a very good way in preventing security vulnerabilities from being introduced in the first place.

The mere fact of deciding to default to using YAML as the default JSON parser without disabling arbitrary object construction is a mind-numbingly dumb decision. What's more fucked up is the fact that this commit actually made it into the code base, and it's not even a recent decision. Apparently it's been in the code base as far back as the 2.3 days. Basically, thousands and thousands of Rails devs have looked and used this "feature" and believed it's not a bug. This is a systematic and cultural failure and crime against reason perpetrated by the Rails community, which also happens to be a vast majority of the Ruby community. It's time you guys learn some proper security 101.

<comment><USER3> I think you're being downvoted because you set up a bit of a strawman there. By my read, the central focus of this article is to explain a very dangerous vulnerability in an accessible way. It didn't come across as having made a lot of excuses.

<comment><USER2> I'm just going to quote directly from the post:

        The holes in Rails XML and JSON parsers for different
        vulnerable versions have been fixed, and some have asked
        why they weren't detected and patched earlier. The simple
        answer is: security is hard. These issues are only
        obvious in retrospect. Rails and Ruby aren't any less
        secure than other frameworks and languages. Security
        vulnerabilities are bugs at their core, and very
        difficult to detect. There is almost guaranteed to be
        insecure software on your laptop/phone/server/garage-
        door-opener somewhere – it just hasn't been discovered
        yet.

I don't disagree with the last 2 sentences. But I don't agree with the first part of this paragraph at all and I have to call it out. I don't agree my example was a strawman, Rails has a track record of introducing 8 code execution vuln and 8 SQL injection vuln. Django has 1.

The fact is, it's not just this article that was making excuses, it's the Rails devs that have their bottom-line at stake that have come out of the woodwork since Jan that have pissed me off. Example: This guy who wrote a response to the "What The Rails Security Issue Means For Your Startup" post:

        http://isotope11.com/blog/what-the-rails-security-issue-shouldnt-mean-for-your-startup

They are also prevalent in the past HN threads discussing Rails vulns.

Note to Rails devs: if you want to secure your bottom-line, you should probably do your part in writing secure(r) software.

<comment><USER3> There's very little you're saying here that I disagree with. I also agree with you on the matter of Ruby on Rails being less secure than other frameworks. A friend of mine quipped the other day that it's time for Ruby devs to "take of their brogramming cool-shades and embrace some of what the neckbeards have been telling us."

I just don't interpret "security is hard" as an excuse. You just repeated the phrase yourself, yet your central point seems to hinge on that being an excuse. You saw fit to put it in its own block and indent it.

There are also a community of Ruby developers who are trying very hard to improve the situation. I'm sure there are more, but the group at the forefront of my mind is Ronin Ruby [1].

In order to improve, we must first admit that we have failed. For my part in the Ruby community, I humbly admit that we can do better.

[1]:<http://ronin-ruby.github.com>

<comment><USER2> The central point I'm trying to make is this article is being disingenuous. Security is hard, but not so hard that a minute or two of thinking and filing a PR won't prevent the YAML exploits. I don't actually think the Rails community is dumb, that's why this whole thing pissed me off so much because the fact that it was even introduced and existed for so long tells me that thousands of Rails devs have just decided to turn a blind eye over the years.

I'm well aware the Ronin guys BTW, I have mentioned them in a comment in another HN thread a few days back and called for help for those 2 guys.

bauland42 who is behind <http://www.rorsecurity.info/> also deserves an honorable mention.

Good luck Rubyists.

</comment></comment></comment></comment></comment><comment><USER4> One of the best lines is: "Security vulnerabilities are bugs at [frameworks' and languages'] core, and very difficult to detect." It's a good illustration of why "security" can almost never be a "feature" added on top of software. I try to help my teams understand that security is an emergent property of the system. Thus, if a system is to be secure, it must be accounted for throughout the design and development of a product.

I once saw an application that used Java's standard PRNG to create session IDs used for authentication after a user logged in. They tried to fix the fundamental security flaw by appending a time stamp, and then hashing that value. The powers at the time didn't believe it was a vulnerability until I did some white hat hacking. I wrote a simple app that retrieved a couple of session IDs and calculated the seed of the random number generator from them. It was a simple step then to guess session IDs of subsequent logins and impersonate those users. They quickly moved to replace the whole design with a fundamentally more secure solution.

</comment><comment><USER5> > _This is desired functionality by the creators of YAML, since it gives > developers the ability to write and read Ruby objects to disk, like an > object database._

It is? I don't think the creators of YAML exactly had Ruby in mind.

This isn't a problem with YAML, it is a problem with how the Ruby/Rails stack _handles_ YAML.

<comment><AUTHOR> "In contrast, YAML's foremost design goals are human readability and support for serializing arbitrary native data structures."

\- YAML Spec: <http://yaml.org/spec/1.2/spec.html#id2763035>

It isn't an issue with Ruby's implementation, but rather the spec itself.

You're splitting hairs here, and ignoring the rest of the article. YAML isn't evil or a "problem" per say. It's only when you pair YAML with un-sanitized user input that we get into problems.

<comment><USER6> The spec seems safe for data models where instantiating a data structure does not mean running arbitrary code (ie, constructors). For example, ML or Haskell abstract data types, or 'bless' in Perl.

<comment><USER3> I'm not sure what you mean by "data models where instantiating a data structure does not mean running arbitrary code". I can't think of a basic data type (a hard thing to define, btw) in Ruby that doesn't have a constructor, but I'm not sure that's what matters. Any attribute assignment in Ruby can be made dangerous if written in a way that wraps dangerous code in a attribute assignment methods.

What I think has gotten Rubyists in trouble is that we forgot YAML really is just serialization. Here's some advice that seems obvious now, but a few weeks ago would have inspired a "wha?" or a "huh?" from a lot of developers.

Don't pass anything to YAML::load that you wouldn't pass to Marshal::load.

Consider for a moment how someone would react to passing anything from HTTP post to Marshal::load. It seems obvious. It is obvious.

YAML is serialization. Whether or not serialization can be made safe is for people much, much smarter than me to figure out.

</comment></comment></comment></comment><comment><USER7> > This behavior isn't inherently unsafe, after all we had to manually build > our exploit string and manually instantiate our class. The problem only > comes when we put all of these things together.

This is very much the wrong way to think about security. Weakness will be exploited. An attacker only has to find one path past your defenses; you have to guard every possible door. Saying, "look how strange and unlikely the interaction of these weaknesses was," misses the point. Code interacts lots of ways, and if it hadn't been this interaction, it could have been another.

Treating user data as untrustworthy is a good start, and you should do that. But you _also_ ought to make each piece of your code base secure and robust, even if you can't think of how it would be exploited, now.

<comment><AUTHOR> That was my implied point of the whole article. An attacker can use your code against you in unexpected ways.

</comment></comment><comment><USER8> If you want the straight-to-the-point version, postmodern wrote up a bit more technical version here: [http://ronin-ruby.github.com/blog/2013/01/28/new- rails-poc.h...](http://ronin-ruby.github.com/blog/2013/01/28/new-rails- poc.html)

</comment><comment><USER9> I'm a ruby noob, but if I read this right it means that when you deserialize a YAML string into an object it will also create methods from ruby code inside the YAML string?

Does a serialization format really need this functionality? it strikes me as something that is clearly dangerous and you would be better simply to deserialize data members?

Is it a common requirement to pass code around as strings?

<comment><USER10> You aren't exactly reading it right. The string is deserialized as a string, specifically as a hash key. However, there are a number of classes in Rails (and presumably other Ruby projects of similar complexity) which can be tricked into evaluating string properties as code when they are created, and ActionDispatch::Routing::RouteSet::NamedRouteCollection is one.

The general lesson taken is not that it is dangerous to ever use eval in any class ever, because that is really useful, but that it is dangerous to allow arbitrary instantiation of classes, because that's pretty obvious even without this exploit. For example, even without code execution you could use this same trick to DoS the server by instantiating a bunch of heavy classes which won't get garbage collected.

<comment><USER11> > The general lesson taken is not that it is dangerous to ever use eval in any > class ever, because that is really useful

Ah, then this is, at its root, a language design deficiency. If there's something useful that can only be accomplished conveniently by calling 'eval' _in a library routine whose job is not specifically to evaluate code_ , then the language needs a better way to do whatever that is.

In the Lisp world we tell people: _never, ever_ call 'eval', the _sole_ exception being that you're writing an interpreter. It is sound advice. Unless you're intentionally writing an interpreter, there is a better way to accomplish whatever you're trying to do.

<comment><USER11> Ah, a downvote. Yeah, I thought this might not be a popular message.

I have many years of experience in Lisp, and what I'm telling you is the longstanding consensus among Lisp experts. You should listen to us. Ruby is not so different from Lisp that the lesson is inapplicable.

I stand by my comment.

</comment></comment></comment></comment><comment><USER12> Security and good software engineering principles are perhaps not the first focus in the Ruby community, and that is a shame. Yes it is nice to have cool frameworks with lots of magic, and we can create a blog in one command. But none of that matters when you end up in infinite patch cycle. Remember poor design choices mean technical debt for a long time.

</comment><comment><USER13> def double(number) return number * 2 end

double(10) # => 20

double("foo") # => "foofoo"

It's called polymorphism. `*` is a method.

Learn how the language works before you write an article about it proclaiming your ignorance.</comment>


Fast strongly universal 64-bit hashing everywhere - <AUTHOR> https://lemire.me/blog/2018/08/15/fast-strongly-universal-64-bit-hashing-everywhere/ <comment><USER3> It's "fast" and "strong"? No measurements are given other than one informal speed comparison against murmur64.

What is the goal, and where's the comparison, relative to xxhash, siphash, highwayhash?

[https://en.wikipedia.org/wiki/List_of_hash_functions#Non- cry...](https://en.wikipedia.org/wiki/List_of_hash_functions#Non- cryptographic_hash_functions)

I'm not sure what the point of the blog post's universal hash functions is, within the domain of practical software engineering. Who's the audience, programmers or number theorists? Who's going to implement this, and how are they going to choose (or write a function to randomly choose) a, b, and c to give at least adequate properties for their task?

<comment><USER4> It's not "strong", it's "strongly universal", which is a well defined mathematical property. Universal hash functions are the basis of MAC part of popular authenticated encryption algorithms, like GCM and ChaCha-Poly1305 (though for this purpose 128-bit tags are preferred).

Though I don't see what the point of this particular implementation is, with its hardcoded keys and fixed 64-bit inputs instead of a random secret key and variable length inputs.

<comment><USER5> Because it's hashing an item of fixed size. This is specialized for 32 or 64-bit integers. clhash supports arbitrary length strings. There is a performance cost for that generality.

For example, in my experiments, clhash is 1/3 as fast as murmurhash or Wang's 64-bit hash for 64-bit integers.

</comment></comment></comment><comment><USER6> Uh, wait a second...

64 bit hashing means only 2^64 . Understanding the birthday paradox ( [https://en.wikipedia.org/wiki/Birthday_attack](https://en.wikipedia.org/wiki/Birthday_attack) ) means that one needs only process 2^32 hashes to find a collision.

Yuck. NO.

<comment><USER7> Not to mention a "fast hash" is inherently insecure, so the author must be mistaken: He should be trying to add arithmetic operations rather than remove them.

<comment><USER8> Fast hashes are not inherently insecure - in fact you want hashing to be as fast as possible. You may be confusing the general case of hashing data, with the specific case of hashing passwords.

The goal of the former is to provide a hash such that the likelihood of any arbitrary objects hashing to the same value is proportional to the square of the hash size (I think?), for cryptographic hashes their are additional goals like the hash output being uniform across all inputs, and that changing any single bit in the input should on average change half the bits in the output, etc. that's because the use case is validation of a single input - <USER2> verifying your download isn't corrupt or whatever.

For passwords the goal is different - you're generally dealing with small objects, and so the cost of an "expensive" hash being measure in milliseconds is acceptable - the expectation being that people generally don't get them wrong. But the attack scenario here is an attacker with a password hash wanting to brute force a large number of passwords to find the one that hashes to it. Given large is potentially very large, it doesn't take a huge amount of time per hash to make the attack non viable.

This particular version is hashing of the former type, without cryptographic concerns. It's goal - to my reading - is to be a hash function that achieves something close to a single bit change flipping half the bits, while also guaranteeing that no two values hash to the same value.

</comment></comment></comment><comment><USER9> From the code:

        long a,b,c; // randomly assigned 64-bit values

As I first understood the way this blog post was written, the author was relying on the JRE to populate these uninitialized variables with random values.

Edit: that was an incorrect understanding. It's not shown in the post, but in his real code he actually does assign values to these (thanks for the clarifying replies).

More of the code for context:

        // in Java, long is 64-bit, int 32-bit

        long a,b,c; // randomly assigned 64-bit values

        int hash32(long x) {
          int low = (int)x;
          int high = (int)(x >>> 32);
          return (int)((a * low + b * high + c) >>> 32);
        }

<comment><USER10> What he means is that to construct a particular instance of this hash function you pick random values a,b,c and do what he describes. You can make multiple different hash functions by picking different random (a,b,c) triples.

Now, there are some specific choices of a,b,c that yield bad results. If you pick a=b=0 then your hash function ignores your data. If a or b is a multiple of a large power of 2 then it ignores many bits of your data. If you take e.g. (a,b,c) and (3a,3b,c') then you get two closely-related hash functions.

The specific property he's interested in here is that of being "strongly universal" which means that when you pick your hash function at random from whatever family of hash functions you've got, "on average" it behaves well. (Specifically, it means that if you fix two possible _inputs_ and vary your _hash function_ at random then collisions between those two inputs are as rare as they should be.) His family is strongly universal despite the issues I describe above because having _enough_ low-order bits be zero to cause trouble is "rare enough".</comment></comment>


Ask HN: Being a learner as a freelancer? - <AUTHOR>

After 13 years of professional activity in IT covering multiple areas (sysadmin, data management, programming, project management) I went back to university to get a Master in CS. It was one of my best decisions! Now, the end of my studies is nearing, and I would like to continue learning in my professional activities as freelancer/self-employed. The problem is, a freelancer is called for his expertise, and not for letting him learn new stuff from the company's employees.
What is great at university is that you learn from people who really know their stuff, and that's what I'll miss most. I've always learn t by myself, but now I would enjoy learning from others, also in my professional environment.
I understand that could not be realistic, but in that case, do you have tips or suggestions?
Edit: shortened text <comment><USER2> Here's some advice that I took from HN a while back and have benefited greatly from:

Market yourself as an expert in your field and not someone that just 'wants to learn,' even if that is your primary goal. Businesses (and any potential clients) are in it for a profit, and they generally want people who appear to know what they are doing rather than someone who is looking to learn. Now, keep in mind that you will most likely be learning a lot through challenges that arise - this is actually the fun part (in addition to the paycheck)!

By now, you probably know a few people in the business. Email, call, or perform any other forms of reaching out to these people. Google "X freelance jobs in X city." Update your LinkedIn. Go to local Meetups (meetup.com is great) and hand out business cards (these can actually be ordered cheaply via internet sales). This may all seem like obvious information to you, but go ahead and add it to what you are already doing.

Also, the most effective resume is a portfolio.

Good luck in your future endeavors!

</comment><comment><USER3> "The problem is, a freelancer is called for his expertise, and not for letting him learn new stuff from the company's employees."

Being called on for your expertise is not exclusive to learning new stuff (from anyone).

I suggest that you begin testing the boundaries of freelancing rather than trusting your own assumptions or the common beliefs of others.

<comment><AUTHOR> >I suggest that you begin testing the boundaries of freelancing rather than trusting your own assumptions or the common beliefs of others.

That's exactly what I plan to do, hence my question, but I hoped to get better tips than 'try it'.... Sharing your experience would have been interesting.</comment></comment>


Math Titans Clash Over Epic Proof of the ABC Conjecture - <AUTHOR> https://www.wired.com/story/math-titans-clash-over-epic-proof-of-the-abc-conjecture/ <comment><USER2> Great read, thanks!</comment>