Promises are not neutral enough - <AUTHOR> https://staltz.com/promises-are-not-neutral-enough.html <comment><USER3> I think the author wants promises to represent computation, whereas they represent predetermined (i.e. single-shot) events. He mentioned C# Tasks, which do mainly represent computation, but in some cases Tasks are also used as events and this gets confusing as hell. I've worked with C# Tasks and hope that MS once cleans this up and builds the stuff on promises instead. Note that the C# language construct uses the awaitable pattern (GetAwaiter method) instead of tasks - awaitables are actually pretty similar to promises.

1\. Eager, not lazy - I think it was a mistake for the promise constructor to take a function, and in that way lead the users to believe the promise represents a computation. Creating a pair of promise and future (the latter as the producer side, like in C++) would be much cleaner. I disagree that lazy would be more general, you can simulate lazyness with functions, but you couldn't eliminate the performance cost of creating the unnecessary closure with a lazy solution. Regarding getUserAge - the common case for that function would be to take the user ID as the parameter (and hence would be lazy by construction), the parameterless version is a special case.

2\. No cancellation - cancellation is much better represented with cancellation tokens (even C# Tasks cancel with cancellation tokens, so does fun-task mentioned at the end, though in non-composable way) - you cannot build a generic solution that can cancel the right computations. With cancellation tokens it's clear what cancels what.

3\. and 4. (as well as being allowed pass non-promises to places where only promises make sense, like Promise.all and await) are unfortunate accidents that make typed environments (e.g. TypeScript) harder to work with but are not that important as 1 and 2.

<comment><USER4> Actually, cancellations can be useful to prevent wasting resources on useless computations. Imagine, if you have 2 threads doing some computations that will be later merged. If one thread fails, it makes no sense to continue executing other thread. That is where cancellation can help - but it should be thoroughly designed, not hacked like they usually do it in Node.JS.

<comment><USER3> I was not saying cancellations are not useful, I was saying cancellations are better handled explicitly via cancellation tokens (which compose perfectly, unlike computation based cancellation).

</comment></comment></comment><comment><USER5> This article doesn't even touch on the worst sin of JavaScript Promises: they swallow errors and exceptions, which makes them nearly impossible to test correctly and makes debugging horrifying (if you even notice anything is wrong.)

Promises are a great example with the problems of believing that something good in one language will be good in another. Promises in JavaScript are fighting the language, because JavaScript is fundamentally a collection of isolated but contextual behaviors.

Using Agents to encapsulate callbacks is easier to reason about, easier to test, less likely to swallow errors whole, and doesn't have any of the problems laid out in this article. Unfortunately because it isn't a model popular in any other language, it doesn't have the name recognition Promises do.

<comment><USER6> Promises don't swallow errors (sorry to be pedantic but there's no concept of Exception in JS). They just propagate it to the rejection channel.

What was your precise experience?

<comment><USER7> If there's no logic to catch the rejection it will be silently ignored, which is effectively the same as "swallowing exceptions" – your distinction is correct but it's academic at best.

In practice, this behavior causes very real problems and in node land they made the sane decision to kill the process whenever an unhandled promise rejection comes about. I don't know if this has landed yet, but you'll see a warning about it if you run a node process in which you reject a promise.

<comment><USER8> This was a problem before native Promises, but not so much anymore.

Throwing on unhandledRejection has been the default in Node.js >= 7 and browsers now have DOM Levels 1 and 3 events for unhandledrejection.

<comment><USER7> This is a half truth. Native promises landed quite some time before a way to catch unhandled promises did, and even that event wasn't enough, as evidenced by at least node deciding that the process needs to crash. (As it would've when unhandled exceptions were thrown.) The browser story is of course different.

Regardless, the original poster was correct – this has been a sin of promises for a long time. What's worse I think is that because of this we move have semantics that are close but not quite the same as exceptions. Case in point: throwing an exception while executing a promise function will reject the promise. But it's not an exception anymore, even though the value of the rejection is in fact the exception. The semantics are now different – because promises.

Promises in JavaScript have a certain almost-but-not-quite quality to them.

</comment></comment></comment></comment></comment><comment><USER9> I don't know. André Staltz is a great programmer, but I can't help but think what seems "opinionated" to him about promises boils down to the fact that they don't perfectly match certain quasi-ideological preferences he has about async programming, at the expense of all other concerns. As he states at the end, promises still work, you can get things done and everything is fine. But the part about them being opinionated I just can't get behind.

In fact, if promises worked the way he wanted them to, it would hurt the ecosystem in every category he mentions. Lazy promises would cease representing a single value, and be un-cacheable. Promises that didn't flatten inner promises would create endless confusion and ambiguity over "onion- promise" scenarios. Sometimes-synchronous promises would introduce subtle and sometimes catastrophic runtime ambiguities (aka "release zalgo"). Even cancelable promises would raise thorny issues regarding whether promises are intended to be multicast or unicast, which is a problem the current design side-steps entirely.

<comment><USER10> This has nothing to do with it. I love promises but they are truly limiting.

Here is an example of how promises limit the power of mobx

[https://twitter.com/spion/status/958906847385341952](https://twitter.com/spion/status/958906847385341952)

Another example relevant in node is continuation-local-storage (equivalent to threadlocal storage). Implementing it on top of generators or other "chainable / thenable" abstractions is trivially easy. Implementing it on top of native promises and async/await is impossible without deep hooks into the platform.

More examples here: [https://spion.github.io/posts/es7-async-await-step-in- the-wr...](https://spion.github.io/posts/es7-async-await-step-in-the-wrong- direction.html) (see the second part)

We should've paused on async-await and waited for jhusain's compositional functions: [https://github.com/jhusain/compositional- functions](https://github.com/jhusain/compositional-functions)

In the meantime generator based libraries would've properly explored the whole breadth of power that co-routines can give you, creating cowpaths to be paved by TC39.

Promises make trade-offs, and they end up with a design that is generally good and can be used well in some number of situations. But not all. Not nearly enough to get first class syntax support that makes them privileged over all other solutions.

<comment><USER11> Gorgi Kosev's post highlights a very nice use case for generators (database transactions). However, in all my JavaScript over the last few years, that is the only good use case I've found so far in my code base for generators. In all other use cases I've come across, async-await works just fine, and has a much nicer syntax to work with.

<comment><USER10> I elaborated that case in the most detail, but there are many other problems that generators solve mentioned in the blog post. Another very common one is getting the current user that initiated the request (or maybe their session), which you need to pass around to all your functions/classes.

What if you could simply `yield getCurrentUserSession` and the engine which ran the toplevel generator returned it back to you?

jhusein's compositional functions solved the syntax issue.

</comment></comment></comment></comment><comment><USER12> The analogies damage this article because they feel wrong. For example the "never synchronous" example is more like this:

 _You order a burger at the cashier window, then go to the pickup window. If the burger is already made, it 's already at the pickup window when you get there._

The author wants a special case where if the burger is already made, they hand it to you immediately at the cashier window. This might seem more efficient, but both in the restaurant and in code it makes logic way more complex.

<comment><USER13> Exactly. If for some reason my promise is immediately fulfilled (e.g. the result was previously memoized), I don't want to provide an alternate codepath to handle the result.

</comment></comment><comment><USER11> I work with fairly large JavaScript codebases, and the issues mentioned in the post has never been an issue for me. The switch from callbacks to Promises, and later to async-await has made a massive improvement to the ease of writing, reading and maintaining the code. Lazy and cancellable tasks are edge-cases that don't need support in Promises directly. I haven't needed either of those in more than a couple of places in the code, versus thousands of places where Promises are used as is.

I can see the automatic unwrapping of Promises to be an issue in some libraries that want to make specific guarantees, but in most of my code this has behaviour has simplified things.

I definitely prefer having Promises and async-await right now over another theoretically sound (but probably more verbose) system available in a couple of years.

<comment><USER14> I think Promises=>async/await works great for certain domains like get a value from a database, them make an insert, then do something else and then return a message to the user.

I however write a lot of systems where almost all operations need to be concurrent, cancelable and rate-limited.

Personally I find callbacks and the event loop easy to reason about, but too daunting when all you do is CRUD requests to a database.

The problem with Promises though is that they spread, they don't like to live side by side with other async paradigms.

</comment></comment><comment> Const-me I don't think the author has experience working with C# tasks.

Technically, the API documentation indeed says a Task has Start() methods i.e. is lazy.

But practically, in the majority of cases they are created already in Running or WaitingToRun state. This applies to tasks returned by asynchronous APIs in the framework, tasks implemented by user-written async methods, and tasks started with Task.Run() static methods. Calling Start() on them will throw an exception complaining about the wrong task state. So, in the current versions of .NET, the tasks are eager just like in JS.

I think the lazy tasks are mostly for backward-compatibility with older .NET framework 4.0 that already had tasks but didn't support async-await.

</comment><comment><USER15> Neither lazy nor eager is neutral. Sometimes you want one, sometimes you want the other, and you can build either out of the other.

For promise cancellation, this has been talked to death, but in short, making any function preemptable at any point in its execution makes writing correct code much much harder. As an example, I've got an API that takes independently cancellable requests. Multiple requests often need to calculate the same thing, so there's a cache. Any given promise in the system might be downstream of multiple requests. If cancellation is built into promises, how do I express how cancellation should propagate through the tree of promises?

A C#-style cancellation token API, orthogonal to promises, is simple, easy to build, and easy to understand.

<comment><USER16> > A C#-style cancellation token API, orthogonal to promises, is simple, easy > to build, and easy to understand.

I'm interested in learning more about this, do you happen to have any links to building such an api?

<comment><USER15> Creating a cancel token gives you two things: a token and a function. You call the function when the token should be cancelled, and the token can tell you when it has been cancelled. The simplest way to query the token is to call token.throwIfRequested(), which throws a Cancel if the token is cancelled. The token can also give you promises or callbacks of cancellation if you like, so you can do stuff like `const result = await Promise.race(token.promise, promiseOfResult);`

So you pass the token into a cancellable API, and that API calls token.throwIfRequested() in places where it is safe for it to do so (i.e. outside of critical sections).

A library implementing this in <100 lines of code (based on a spec that has since been sadly abandoned): [https://www.npmjs.com/package/cancel- token](https://www.npmjs.com/package/cancel-token)

<comment><USER16> Oh I like this. It's pretty simple, both code and conceptually, that it seems obvious in retrospect.

It allows a function to be cancelled from the outside, but only on its own terms, and gives the function a chance to clean up after itself.

I'm going to use this

</comment></comment></comment></comment><comment><USER17> If promises were lazy (basically just function composition), it seems like we'd have similar problems to Haskell where it's difficult to understand performance. You wouldn't know when an I/O operation starts or whether it will get executed again. Maybe that's okay for a high-level API, but low-level I/O operations are not idempotent, so this seems risky?

So this looks like a trade-off: you could make function composition easier only by making Promises less suitable for their original purpose. By going generic, you lose an important guarantee that a Promise is just a value.

</comment><comment><USER18> const myJob = { run: () => fetch(... is too long? Eager is easy to make lazy. While the opposite is also true, it means a superfluous run. It is worse, semantically speaking. I'd argue that eager is more general than lazy.

Also, cancellation is yet another state and it's hard to generalize especially when you don't have threads.

Promises should always be async because you'd want the result to be consistent. If I'm returning a promise and you are depending it to be sync, that means it weakens my flexibility. It makes the code harder to reason about.

</comment><comment><USER19> OK, we can't do threaded imperative programming because threads are expensive and people botch the locking. So we have callbacks where completion of some external event calls you back. Then you need closures so the callback has some state so it knows what to do when called back. Now you have a control structure problem, and need a state machine to decide what to do next. But most of the time you just want to do the next thing, so there's syntax such as ".then()" so you can write imperative programs again.

</comment><comment><USER20> One of the big challenges mentioned with promises is that you have to kind of commit to promises linking to promises... this is a common problem with async systems added later, where you have to "line them up like gears", and you can't just do async functions which call non-async functions which call async functions and expect it to work. Python has this problem too.

There's a solution in delimited continuations, however delimited continuations seem to only be used and understood in the Scheme community (are they used anywhere else?). Delimited continuations allow you to suspend your code to a "prompt" lower in the stack at that point... and it doesn't matter if you have non-"async" code in between.

It'll be nice when they make their way to other more mainstream languages.

</comment><comment><USER21> \- Eager is relatively easy to convert to lazy, if needed. The inverse isn't true.

\- async is relatively easy to turn into sync. The inverse isn't true.

\- no API design is "neutral". Any API design is opinionated. Cancelable lazy synchronous promises is just as opinionated a design as the current design.

</comment><comment><USER22> The whole idea of cancelation is poor; it shouldn't even be a feature.

The way you avoid unnecessary computation, when you have laziness, is to just roll it into the lazy semantics.

Have it so that if the promise generates something complicated, like a sequence, that the promise only generates as much of that something as is accessed (and maybe only a little bit beyond that).

In other words, the async promises should perhaps behave not so differently from synchronous lazy mechanisms.

The two are flipsides of the same coin. Say I have a synchronous lazy list (of strings). The strings come from reading a file. Ah, but reading a file is asynchronous at the OS level. So actually the list is asynchronous, in a sense. When we access the first element in the list, a line is read from the file. The underlying stream object reads an entire buffer-sized chunk, though: still synchronously. Moreover the OS behaves asynchronously and reads ahead in the file, caching more of it than the stream library asked for. Of course, the OS doesn't read the _whole file_ (unless it's small). Just a little bit ahead. Enough ahead not to hammer the I/O subsystem with lots of small operations.

We can create this list over a log file that has 100 million lines, then read just the first 100 lines and stop using it. The underlying stream library might read 16K of the file, of which the 100 lines occupies only the first 8. The OS might have read ahead by quite a bit more than that and cached more of the file, and the hard drive's firmware might have buffered an entire track. If we don't read anything more from that list, then the operation is effectively canceled. The OS won't cache any more from the file; the stream library won't buffer more of text stream.

<comment><USER23> > _Have it so that if the promise generates something complicated, like a > sequence, that the promise only generates as much of that something as is > accessed (and maybe only a little bit beyond that)._

What about when you start composing Promises? For example, say I have a top- level promise that just returns a value. But under the hood, it needs a promise that generates an array. Even if the under-the-hood promise generates the array piece by piece, the caller of the larger promise is only even going to get a single value, so they lose the ability to "stop".

(You might imagine that the composed-over promises are network I/O, for example.)

<comment><USER22> In that case, one thing we can do is that the under the hood promise is not forced at all if the wrapping promise's value isn't forced yet. Then we don't have any async behavior, unfortunately; no calculation begins until the promise is called in. We can do part of the calculation ahead of time, but then stop and don't complete it until there is an indication that the value is required. That is fudgy though: how far is far enough to reap the async benefit without the downsides.

How about this alternative: instead of .cancel() on promises have a .commit(). This is called if you're sure that you will eventually need that value. The calculation then proceeds full steam ahead: no going back.

You can ask for the value with or without .commit(); it is just a hint. But if you ask without .commit(), you may have to wait for a completion that was deliberately stalled due to your lack of commitment.

Without .commit(), async promises will still proceed on their own to some extent based on some fudge factor; we don't want programmers automatically calling .commit() on every promise they make to get the async benefit, which defeats the purpose.

Uncommitted promises could be identifiable to the garbage collector and subject to an internal cancelation protocol between GC and the promises. That protocol basically helps the promise's thread vacate the object so it can be reclaimed.

Promises could have some sort of hint about how far to proceed before requiring commitment. This would have to be well thought out: such hints tend to be too system and workload specific. Automatic tuning is better. The promise system could keep some statistics about how soon various kinds of promises are called in after being initiated, and how often they are called in _at all_ , and then uncommitted promises could decide based on that how far to compute.

</comment></comment></comment><comment><USER24> This was a weird blog post to read. I think I agree on all of your points (Promises should be lazy[-ish], cancellable and optionally synchronous) but disagree on all of your proposed solutions.

I do think `p = new Promise(fn);` shouldn't kick off the `fn` immediately. But that it should start right away in the next event loop. I haven't had issues with creating promise getters for repeatable calls. And think it organizes the business code away from the low level code.

I don't see a problem with the original Promise.cancel() you proposed or how your lazy promises makes canceling them any easier.

And don't we have `await` for the synchronous problem?

      console.log(await Promise.resolve('hello'));
      console.log('world')
      // outputs "hello" "world"

<comment><USER25> One nice thing about "new Promise()" calling the function immediately is that if you are prepared to provide the value immediately then you don't have to return into the runloop, but probably the reason I'd give for why delaying the call would be a horrible idea is that the vast majority of the time the promise is going to do some minimal amount of setup work and then... return to the runloop (and if it isn't, I am going to ask why you are using a promise). That means that the behavior they currently have of calling the function immediately minimizes returns to the runloop and provides performance as close as possible to what you would get if you hand-coded it using callbacks (the only overhead being the unlikely-to-be-optimized-away-fully-by-the-VM object allocations and indirect function calls; but like: this paradigm in a language with zero-cost abstractions would be perfect).

<comment><USER4> No, you won't get the result immediately anyway because then() callbacks are called only on next event loop iteration. This protects from overflowing the stack, but might have small impact on performance.

<comment><USER25> OK. I see why they would have chosen to do that, and it disappoints me, but it seems to be weirdly more complex than that... this code prints the numbers in order (and continues to do so if you rearrange the calls to setImmediate and setTimeout or move them outside of the function either before or after)... so it is definitely returning from the function but it seems like the resolved value gets to jump the queue?

        (async () => {
            setImmediate(function() {
                console.log("4");
            });
            setTimeout(function() {
                console.log("3");
            }, 0);
            console.log(await new Promise((resolve, reject) => {
                console.log("0");
                resolve("2");
            }));
        })().catch();
        console.log("1");

<comment><USER4> I don't think it is a good idea to expect some specific order in executing callbacks here unless it is described in some specification.

</comment></comment></comment></comment></comment><comment><USER26> You don't have to use promises: [https://firstname.lastname@example.com/continuation- passing-style-p...](https://firstname.lastname@example.com/continuation-passing- style-patterns-for-javascript-5528449d3070)

</comment><comment><USER27> Agreed 100%. I did a bunch of js work about 3 years ago, used tons of promises. Then started a new job using Scala. The futures api in Scala is exactly what the author advocates, and it is definitely better for the reasons he gives.

</comment><comment><USER28> He actually missed my least favorite thing about the promise API, which is that they fail silently. I'd argue that by default, an unhandled rejection should throw an exception at the end of an event loop, with an opt-in for the current behavior per-promise.

</comment><comment><USER29> I seem to be in a minority, but rarely do I want to use the `new Promise()` mechanism for creating a promise, and I get the distinct impression that having it be the 'default' is a bad idea -- the amount of times I've seen people wrapping up all their promise-related code inside the constructor, finishing off with `.then(function (x) {resolve(x)})` is disappointing :(.

async/await solves much of this, of course, but where that's not available I much prefer to keep all my async functionality actually async, and start off by using `Promise.resolve()`. Save the constructor for when you need to encapsulate some non-promise async code.

</comment><comment><USER30> Sindre built some great little modules that make working with promises a breeze. [https://github.com/sindresorhus/promise- fun](https://github.com/sindresorhus/promise-fun)

</comment><comment><USER31> This feels analogous to the problems w/ futures in Scala/Java as they were first introduced. And the solutions are provided by libraries like [https://monix.io/](https://monix.io/).

So why are promises the problem instead of the lack of libraries on top of them? I understand cancellation cannot be fixed, but laziness sure can. As for synchronous execution, that's just not gonna happen in event-driven-land. It doesn't with other callback-based APIs (except AJAX which is deprecated) and I don't see the complaints there.

</comment><comment><USER32> It sounds like the author wants coroutines, not promises.

<comment><USER6> Exactly.

I've been using redux-saga a lot recently and they really fill the gap between the concept of a long running Task and asynchronous values/executions (Promises).

<comment><USER8> I just got there myself after thinking async/await would be all I needed.

There's still a lot that can be done with generators, so I don't see them falling out of favor completely yet.

</comment></comment></comment><comment><USER8> You can have your referentially-transparent cake and eat it too, but the main problem is that no one has developed a decent library that marries FantasyLand-compliant wrappers with Promise interop.

There are about 8 million Task/IO monad implementations and no one stopped for a second to think that `task.fork` could just return a Thenable and work with async/await as expected.

<comment><USER33> This does exist: [https://github.com/fluture- js/Fluture/blob/master/README.md#...](https://github.com/fluture- js/Fluture/blob/master/README.md#promise)

Future.of(0).promise().then(console.log);

</comment></comment><comment><USER34> What do you think of C++17 coroutines? Of particular interest to you might be CppCon talks by Gor Nishanov, which are on Youtube.

</comment><comment><USER35> I rarely use promises. Just for the most simplest logics. When a package return promises in its API, I just use Rx.Observable.fromPromise(theRomise) . ReactiveX, especially RxJS, is so powerful to handle async events, from differents sources to differents logics. You can build powerful pipelines with it.

</comment><comment><USER36> A reminder that LazyPromises are trivial to implement:

        function LazyPromise(executor) {
          this.then = (resolve, reject) => new
        Promise(executor).then(resolve, reject);
          this.catch = (resolve, reject) => new
        Promise(executor).catch(reject);
        }

</comment><comment><USER37> I don't agree. Promises are a solution to the asynchronous callback world, not a dream spec someone came up with.

1\. Eager, not lazy: Why is lazy better? Sometimes I want eager, I use promises. Sometimes I want lazy, I use a promise getter. Done. What if it was the other case, how would I turn a lazy promise into an eager one without messy code?

2\. No cancellation. Events that permit cancellation are rare. Situations in which you would want to cancel something are rare. If you face these, use a promise library that does permit cancellation. Bluebird does it.

3\. Never synchronous. If you want synchronous, just don't use a Promise, use a function that takes another function. I don't get the point about "callbacks to sync". Callbacks are asynchronous. "Synchronous callbacks" may have this name, but they're not actually callbacks, they're functions. A function can take another function as a parameter, that doesn't automatically make it into a "callback".

</comment><comment><USER38> This article is a bit similar to one I wrote a while ago: [https://medium.com/@avaq/broken- promises-2ae92780f33](https://medium.com/@avaq/broken-promises-2ae92780f33)

</comment><comment><USER39> > They force some behaviors to always happen even when it doesn't make sense. > That's okay

No, it's not. Promises suck, that's all, no need to spend more words on it.

</comment><comment><USER40> The alternatives look nice. There's just one requirement missing: streaming progress information to the listeners.

<comment><USER41> I think this completely changes what you have though, in a way that's no longer a primitive?

You then have something like an async generator, which is like a fusion of asynchrony and sequence?

Except generators are pull not push, so instead you have a promise that accepts a function that operates on a sequence?

I don't know if this pattern is common somewhere, someone who does please explain!

<comment><USER40> In Haskell you can generate a list lazily. The last item in the list could be the final result-value, whereas the leading elements could be the progress information. Canceling a computation could be done simply by stopping to "listen" to the result (i.e. stopping the evaluation process).

I guess you could implement this in JS by having a promise-like structure which returns a tuple containing progress information AND a promise for the remainder of the computation. In a sense, this is similar to the generator approach.

One problem is if your program execs an external program. At what point should a promise kill the external process?

</comment></comment></comment><comment><USER42> I thought 'await' was supposed to solve the problem of going from async to sync?

</comment><comment><USER43> Is it possible to modify Promises to be lazy through a Proxy?

</comment><comment><USER44> agreed with OP: being able to call async functions from 'normal' code would be very kind of js.

res=fetch('i-just-want-the-result.com')

at least when scripting with node.js

</comment><comment><USER4> I have a feeling the author doesn't understand Promises well. In my opinion, they are in fact designed poorly, but I don't see any problems with points the author describes.

He doesn't like that the callback is called immediately - but Promises just represent a result that will be available later and do not guarantee (and should not) when the function will be called. If you want to delay some function call, do it explicitly or use a delay promise.

In my opinion, the main problem with promises is broken error handling. They don't play well with exceptions. For example:

        var p = Promise(function (res, rej) {
            throw new RuntimeError("System is broken");
        });

This code will just ignore the error. While it is expected that the runtime error would float up and terminate the program - that is what runtime errors are made for.

This also makes writing tests more difficult because tests often use exceptions to indicate failure.

I have some ideas how to fix it (neither is perfect), but the comment will become too long.

<comment><USER15> This is a fundamental issue with multitasking, in that an independent task will have its own stack, so errors can't propagate up the stack of the function that started the task.

e.g. consider this python code:

        def start_task():
          Thread(do_task).start()

What happens if do_task throws an exception? The exception can't propagate up from start_task because start_task may have returned when the exception is thrown.

At some point you have to join your threads, and you have to await/then your promises, otherwise there's no well defined place in your program for the exception to go.

Linters can help with this. tslint is one example, I believe it can give a warning for unhandled promises.

`p` will reject, any function that awaits p will reject. The error is that you've fired off an asynchronous task but you don't have any code that cares about the result of that asynchronous task (and the stack of any code that

<comment><USER4> > otherwise there's no well defined place in your program for the exception to > go.

I don't see the problem with that. Unhandled exceptions can occur at any place of your program.

I also don't see why the unhandled exception from the background thread cannot terminate main thread. Why not? That is how unhandled exceptions are supposed to work. Terminating the program is the optimal _default_ behaviour for any error in my opinion. This way you won't miss them.

<comment><USER37> In Go there are two ways you can handle wrong things happening:

      - return an error
      - <USER2>

What is the recommended, best, pretty, beautiful way? Return an error. Nobody uses panic.

Javascript Promises are equivalent to that.

If you want to stop the main program, call process.exit(). That would be the equivalent of panic.

<comment><USER4> I don't think it is beautiful. This fills program with lots of unnecessary `if`s for checking results of each call. Also, it prevents one from chaining functions like a(b(x)).

<comment><USER37> Yeah, you're right. Not beautiful and prevents chaining. But works like a charm, don't you think?

Chaining functions is for functional people. Go is proudly imperative.

</comment></comment></comment></comment></comment></comment><comment><USER45> This may get me in hot water here but...

I started working with JS promises specifically when they were barely available in a beta runtime. It took me over a year of working with them to really get a feel for them, now it's been far longer. That's because while you can "understand" the description and use it just fine, but a deeper comprehension and intuition takes much more time. I experimented a lot and insisted on writing my own helpers from scratch, without looking up other people's code, because I wanted to get a _feeling_ for the details.

This article seems quite artificial to me, the problems mostly made-up.

I don't see the point of the first complaint. If you don't want to start right away chain it to something that it should wait for. If it should not wait, then it can start right away. Her writes " _Functions rescue us in this case because functions are lazy._ " which I don't quite understand: what is he running through promises if not functions? Hi "betterFetch" example mixes synchronous and promise syntax - how about using async/await if you prefer the former? I admit though I don't quite get the point of that example.

I don't understand the whole "run a promise" idea either - because you don't "run a promise", that whole notion has nothing to do with what "promise" means. Just look at the word! It represents a (wrapped) future value. Where does the idea of "running it" come from? How do you "run" a (future) value?

You have a function and it is quite easy IMO: Using a promise you chain it to whatever you want to wait for. These days you can even use semi-synchronous syntax (async/await). "Running a promise" makes no sense to me, you run functions, and I don't see where the difficulty lies here?

The second point, cancellation, has been discussed very, _very_ thoroughly - after all, this was on the table to be standardized. One of the issues he raises is the same as point one - if you have a chain it's automatic. The main issue of cancellation is that you have zero control over the actual asynchronous operation that the promise actually stands for - because this is controlled by the OS alone! If you started I/O, what does "cancelling the promise" mean?

1\. If it is still waiting: If you don't want to run something make sure the previous step returns a rejected promise. You can easily "cancel the promise". Just let your promise function check something in the parent scope (via callback or it is in its lexical scope) when its chained function starts, and if that says "you are canceled" then don't do it. You can put such a check as a standalone function anywhere in the promise chain you created, just let that "amIcancelled()" function throw or return a rejected promise. The whole chain aspect is something that the article is missing.

2\. If the code is already running: you cannot cancel the actual (OS controlled) asynchronous operation, nor can you cancel a running JS function (unless you use async/await see bottom paragraph).

I agree that promises are not perfect, but async/await - not mentioned at all! - makes it a bit easier for many people - as long as they don't forget one thing: Even if your functions now look like synchronous ones there is a fundamental difference: A synchronous JS function is never interrupted by any other code. An async function is suspended and other JS code gets to run in the middle of it when it encounters an "await". This is something new first introduced by generators, before that JS functions were atomic (now some are not).

<comment><USER46> > It took me over a year of working with them to really get a feel for them

I had a similar experience. It took quite a while for me to stop shooting my foot. My takeaway from that experience was that, while they do have certain advantages, Promises suck. Any abstraction that is so unintuitive that it takes beginners dozens or hundreds of hours to master is probably not an abstraction worth using - especially if it is supposed to be a primary feature of the language.

<comment><USER14> I had the same experience with the callback pattern. It literally took a whole year to grok. And I code almost every day. I'm now a _ninja_ with callbacks. So it's a hard to motivate myself to learn Promises. Syncronious code is more easy to deal with, and you get concurrency by thread abstraction. But it will eventually bite you when you start to get double transactions eg line 1 checks if there's funds in the account, line two draws money, line 3 inserts good. But then another thread takes the money between line 1 and 2. And then the "single threaded" event loop actually becomes easier to deal with then making sure your code is "thread safe" with locks etc.

</comment></comment></comment><comment><USER47> Promises are classic JS clusterfuck. Replace something shitty, like nested callbacks, with something even more shitty. Thereby ignoring 40 years of experiences of other languages.

<comment><USER48> I couldn't disagree more. Promises are magnitudes better than nested callbacks.</comment></comment>


Yarn determinism - <AUTHOR> https://yarnpkg.com/blog/2017/05/31/determinism/ <comment><AUTHOR> TL;DR:

NPM's shrinkwrap / package-lock.json contains the hoisting positions (folder structure), which Yarn doesn't. Yarn's hoisting positions are only deterministic for the same version - it may vary across different versions. On the other hand, Yarn's lockfile generates much cleaner diffs.

No comment here on the actual implementation of NPM 5 installation process. I've seen comments that installation order had an effect on NPM's folder structure - is this still true?

My main issue with NPM in the past (version 3) were that it was very difficult to actually keep the (1) package.json, (2) the shrinkwrap file and (3) the node_modules folder in sync - any small mistake and one or two of those would become inconsistent, usually requiring a re-install of node_modules, and often even deleting and recreating the shrinkwrap file. Yarn has never given me issues like this.

What is the experience like now that NPM 5 has been released?</comment>


Ask HN: Do you have any code currently in production that you're ashamed of? - <AUTHOR> For instance, code you wrote when you were a beginner on your first projects. Do you have code out there, running right now, that you "learned on"? Have you ever written anything that you're sure somebody out there is maintaining and hating your guts? <comment><USER2> Every programmer has a little sewer system buried beneath them. Each morning it routes digital sewage to and fro, through home built mashers and sprockets and fittings and tubes and gears. It feels good to see a brand name logo on some of these devices, a self given high-five for doing it the Right Way, but invariably there is some lopsided widget bolted to the side with the signature of self.

It's always shameful to know this little sewer exists beneath us, our livelihood, our brain babies. To think of all the twists, turns, and dead ends, unknown waterfalls and geysers yet to be filed away in bug reports, it's sobering and makes the best of us self-hating.

To peruse other's code, seeing beautiful order, a source code aira, a single author's master work in symbols... is always the shortest path to shame in our own designs. Our intimacy with our own creations magnifies our self critique.

Of course I am ashamed of my code. It's the inverse mold of the wrinkly shape my brain is, and for others to see it is the barest, weakest thing I can share. One that I am paid to every day.

<comment><USER3> Very poetic. I think my satisfaction with the code I write depends largely on the environment it was produced in. For code I write for myself, I'm generally fairly pleased at the time I write it. Of course this changes years later when I look back and wonder what possessed me to construct such a monstrosity.

On the other hand, I find code written for pay is often uglier than I would like at the outset due to time constraints. But if it gets the job done and has high likelihood of being scrapped completely (as can often be the case when experimenting in a startup) then that's a tradeoff I'm willing to live with.

</comment></comment><comment><USER4> Yes, most of it :)

And I'm going to leave a lot more when I switch jobs in the near future.

On the other hand, I had to maintain some awful spagetti code when I was starting out (and much of that was written by the current CTO!), so what goes around comes around.

I'd like my successor to hate my guts a little less, but sadly I won't be around to explain why I made several suboptimal (or flat out wrong) choices - most were of the "you have one week to implement this" variety, but others were just me dropping the ball.

When I give my 2 weeks' notice, should I go around all of my old code adding comments? Documentation? What would you like if you were to maintain some spagetti VB6 and .NET code?

<comment><USER5> This is a terrible attitude and I hope I never have the displeasure of working with you. "What goes around comes around" is a horrible attempt at justifying doing poor work. Please see the following wikipedia entries:

[http://en.wikipedia.org/wiki/Abuse_defense](http://en.wikipedia.org/wiki/Abuse_defense)

"""it also refers more generally to any attempt by the defense to use a syndrome or societal condition to deflect responsibility away from the defendant. Sometimes the concept is referred to as the abuse excuse, in particular by the critics of the idea that guilty people may use past victimization to diminish the responsibility for their crimes."""

[http://en.wikipedia.org/wiki/Learned_helplessness](http://en.wikipedia.org/wiki/Learned_helplessness)

"""Learned helplessness is a mental state in which an organism forced to endure aversive stimuli, or stimuli that are painful or otherwise unpleasant, becomes unable or unwilling to avoid subsequent encounters with those stimuli, even if they are escapable, presumably because it has learned that it cannot control the situation."""

[http://en.wikipedia.org/wiki/Rationalization_(making_excuses...](http://en.wikipedia.org/wiki/Rationalization_\(making_excuses\))

"""In psychology and logic, rationalization (also known as making excuses[1]) is a defense mechanism in which perceived controversial behaviors or feelings are logically justified and explained in a rational or logical manner in order to avoid any true explanation, and are made consciously tolerable – or even admirable and superior – by plausible means."""

Please grow up and learn to do the right thing, or at least make an _attempt_ at doing the right thing instead of rationalizing "there was no other option, they made me suffer now you suffer, appeal to authority: but the CTO did it".

<comment><USER6> I don't think it's an attitude that anyone takes pride in. I can very much relate to the parent. You join a company, find out that their code-base is in an abysmal state (I would wager this is the case at most enterprise outfits with aging code) and go to work. Initially, you try to sound the alarm to your superiors, put in overtime to try and improve the code-base "bit by bit". However, over time you find out that those who are calling the shots (driven by business priorities) absolutely do not care, because the code does, more or less, what they say it will. Your co-workers have either always written poor quality code or have long-since resigned themselves to doing so. Remember, you can be in situations where "technical debt" can take months, even years to culminate into stuff hitting the fan, so management can remain blissfully unaware of what is brewing for long periods of time.

I've been in these situations before, where your pride for writing good code is eventually beaten down by your surroundings and the conflicting priorities of management. I've always been uncomfortable in these situations and found a way to leave, but I don't think that means that I have a "terrible attitude".

<comment><USER4> Thank you.

Yes, I work for a subsidiary of a large corporation, the main system is built on a 20-year old deprecated development environment called Forte 4GL ( [http://en.wikipedia.org/wiki/Forte_4GL](http://en.wikipedia.org/wiki/Forte_4GL) ) with more than a million lines of code.

Technical debt is humungous, but nobody cares since they just put more programmers to work (it's indescribably inefficient).

I did manage to migrate one of the core subsystems (policy issuance and data entry) to .NET, and I'm pretty proud of that, but my work wasn't recognized by management, which spends all the time in politics (and advancement is only a function of office politics, etc.). Management is pretty short-sighted, prioritizing short-term shiny new things over fixing problems that will blow up long term.

So after 5 years I'm leaving, I should have left much earlier but well paying, stable jobs are not so easy to get in my country, and I didn't want to emigrate and was afraid of change.

My prediction is that the mismanagement will blow in managements' face when the next economic crisis hits (currently my country is in an economic bonanza which masks bad decisions and mismanagement).

</comment></comment></comment></comment><comment><USER7> Somewhere out there is the second program I ever wrote professionally. Within that program is the greatest SQL injection vulnerability of all time. It's not subtle issue where I forgot to properly sanitize user inputs. Nope, it's a page that explains in big, bold, friendly letters that any text you type into the text field will be immediately sent raw to the server for processing. It might be the world's only self-documenting SQL injection exploit.

I tell myself that the page is only accessible through the administration interface, which is itself only accessible from the sysadmin's computer and with a proper password. I remind myself that there is no PII in the database and that the worst case scenario, if the system is cracked, is that someone has to count all the desks in the building again. I remember the fact that the client specifically wanted this feature.

But, mostly, I just console myself with the knowledge that my name isn't in the source code, so whatever poor bastard is maintaining that monstrosity now can't track me down.

<comment><USER8> I once wrote a parameter parsing function for a home-grown web framework (first job, over ten years ago). One of its features was correct handling of parameter arrays, e.g. foo[42][]=bar&foo[42][]=baz would be converted into {foo: {42: ["bar", "baz"]}}.

In my naivety (but laudable desire for code reuse and simple implementation(!)), I parsed the brackets using eval. Without sanitisation.

Parameter parsing using eval. Who would do that?!

Fortunately no-one ever passed foo[42][system("rm -rf .")]=whoops and really ruined our day...

And the worst thing is, this was only noticed by someone in their job interview, when we asked them a question based on our production code. They got the job :)

</comment></comment><comment><USER9> Yep! Lots of it, actually. The biggest thing you learn is that it really hurts the ability to extend and grow the feature set of the product over time, but that if the product never gets off the ground that never becomes a concern.

But by writing and deploying crap code, you also learn how to write it better the next time. I think most devs know by now how much bad code runs the world.

<comment><USER10> I agree on both points.

You definitely need well structured code to make long term maintenance and development feasible.

But, what is "well-structured" when your app is only in your imagination and subject to many pivots?

The way I proceed now:

      1) write crap fast to get it out there and to also
      discover the potential pitfalls of a given structure
      2) refactor as necessary (it takes off, pivots, performance
      blows, or it's a maintenance nightmare). Be fearless about
      junking entire parts of your old badly designed code if you
      think you are going to maintain this for a while.
      3) see 2)
      4) see 2)
      5) Now that I have a stable structure I keep refining the
      code base knowing that even if it doesn't seem perfect, the
      way I've structured your code is the result of several
      iterations, and is therefore probably as good as it's
      going to get.

[edit for formatting.]

</comment></comment><comment><USER11> I wouldn't trust/believe a programmer who isn't ashamed of something they've done.

In an embedded system, I couldn't think of a nice way to check for the start+end dates for daylight savings time, so I just hard-coded arrays with 10 years worth of dates.

I was eventually surprised to find out that (A) in 2005 the US changed the DST dates, and (B) our system actually survived longer than ten years.

<comment><USER12> I got some code deep in a system somewhere that parsed dates. There are some limitations to the method, but I figured (and still do) it is pretty low risk.

The comment above that code is // god help us if this line is still being run 80 years from today.

</comment></comment><comment><USER13> Pretty much all of it and for several reasons. 1, I keep learning new stuff. 2, for it to be in production means there comes a point where I have to stop refactoring it. So although it could probably be leaner and cleaner, at least it is in production. The code I do for myself that I want to be really proud of never ships because there is always something else to do.

</comment><comment><USER14> If you're not ashamed of it, then you've learned nothing since you wrote it.

<comment><USER15> I don't agree. 'Shame' would require substandard work, indefensible decisions, incompetence — and I'm pretty sure none of my Commodore PET BASIC programs survive.

Is there code I would write differently today? Certainly, plenty. That's nothing to be ashamed of. Time passes; tools change, environments change, requirements change, and available knowledge changes, including papers published by people who have explored relevant problems in different ways. Is there work I would have done differently if not for management constraints? A little, but that's not for _me_ to be ashamed of. Is everything I've ever done the best possible under the circumstances? Undoubtedly not. Is that shameful? No, that's programming.

      By June 1949 people had begun to realize that it was not so
      easy to get a program right as had at one time appeared. I
      well remember when this realization first came on me with full
      force. The EDSAC was on the top floor of the building and the
      tape-punching and editing equipment one floor below on a
      gallery that ran round the room in which the differential
      analyser was installed. I was trying to get working my first
      non-trivial program, which was one for the numerical
      integration of Airy's differential equation. It was on one of
      my journeys between the EDSAC room and the punching equipment
      that "hesitating at the angles of stairs" the realization came
      over me with full force that a good part of the remainder of
      my life was going to be spent in finding errors in my own
      programs.

— Maurice Wilkes, _Memoirs of a Computer Pioneer_ , MIT Press, 1985, p. 145

<comment><USER14> That's fair. 'Shame' is a bit harsh, really. 'Would write differently today' would be better, but doesn't roll off the tongue as easily :)

</comment></comment></comment><comment><USER16> _looks at what was deployed this morning_

Yep.

</comment><comment><USER17> As every programmer, I think my skills have improved over the years so yes, the code I do write now is far better than the one I've written years ago.

I'm not really ashamed, as I do not hear about the "bad" code I've written. However, when I look behind and assess the quality of code I've been working on, I sometimes say to myself that my code "was not that bad".

It has been mentionned a couple of times but now, I like to focus my efforts on code maintainability. I know I'll leave one day or another my current job and I do not want to be hated like the ones I've hated in the past (or in the present...) for having written shitty code.

There one another habit I've adopted quite early : when I was writing some bad code and I knew it, I always tried to let a comment explaining why I chose this path and what I would have done provided the conditions were different. I could have written sub-par code due to code consistency with the other parts of project, to lack of time, sometimes because the implied refactoring would be to huge to handle in a given timeframe, or because a side effect is likely not to have been anticipated at first sight, whatever.

At last, the guy who will pass behind me will know my point of view, and even get some hints of what should be done in an ideal world. And I liked to put some jokes or funny sentences. Those, I've heard about later after my contracts.

</comment><comment><USER18> No.

I have code I would write differently now. I have code I don't like. I have code that I know doesn't quite work all the time. I have code that I would have written differently if I had more time.

I _don 't_ have code that I knew was wrong to write the way I wrote it when I wrote it, given whatever priorities and constraints I was working under.

It probable helps that I don't see my job as writing software. I see my job as making sure the rest of the team can be as effective as possible, and two imperfect programs that save 30 minutes each despite crashing occasionally are better than one perfect program that saves 45 minutes and never crashes.

<comment><AUTHOR> > I don't have code that I knew was wrong to write the way I wrote it when I > wrote it, given whatever priorities and constraints I was working under.

I didn't intend to ask about code that you knew was wrong at the time. I meant to ask about code that was the best that you knew how to do, but given what you know _now_ , makes you feel embarrassed. This is something that I'm struggling with.

<comment><USER18> No, that doesn't bother me. I'm perfectly happy to say, yes I wrote this, yes it's crap, this is why it's crap, here are some ideas for fixing it, here's why I _haven 't_ fixed it.

 _Good judgement comes from experience. Experience comes from bad judgement._

 _God grant me the serenity to accept the things I cannot change, the courage to change the things I can, and the wisdom to know the difference._

 _The reasonable man adapts himself to the world. The unreasonable man adapts the world to himself. Therefore all progress depends on the unreasonable man._

Much of programming can be viewed as adapting the world. It's not reasonable. It requires experience. Gaining experience will _inevitably_ show that what you've done in the past could have been done better. You can't change what was done, so don't bother worrying about it. You can _learn_ from what you did, but recognizing the problems is how you _know_ you've learned, how you know you've improved your judgement. This is a _good_ thing. Don't try to hide it. So you didn't spring into existence already knowing perfectly how to adapt the world? So what? You're not supposed to.

</comment></comment></comment><comment><USER19> No, I wouldn't say ashamed.

When I first started out, I would not let there be something I didn't have some knowledge about. So, after work I'd go home and study or work until I understood everything I could about my position. For me this was classic asp, cold fusion, sql server, db2, informix, perl, and other technologies for CRUD applications, reporting and data management.

I always tell all the developers I work with, If everything you put into production is "well thought out" and has reasoning behind your decisions, then I don't mind if it is not the design pattern I would choose, or uses the exact style I prefer.

</comment><comment><USER20> You bet. My early code sandboxes are horrendous to look at and probably headache-inducing for anyone who might try to decipher them.

I remember one small PHP-based random avatar generator I wrote several years ago. Even at the time of writing I understood how horrible it was. Nowadays I'm afraid I'll have a stroke just by looking at it.

But those were the past days. Nowadays i know better thanks to some great people out there with smart things like SOLID, OOP etc... . I wonder what i'll think of my current code in a year. Most likely will be embarrassed by some of it anyway - there's always a better way to write something.

</comment><comment><USER21> A ton. None of it is owned by me though. My own code, that I own (open sourced mostly) is actually well tested and amazing.

That's because I can take my own time and sit down for four hours and just think of stuff like naming functions or selectors.

I could spend time on Twitter and/or IRC and ask people about gists of code and their opinion of it. Imagine it being user testing, but code base. Basically reviews.

I would be able to go back and just rewrite it for the sake of naming sakes.

No employer would ever approve of that.

That's why my own code is always going to be better than the one I am paid for. No one wants to pay for that.

</comment><comment><USER22> I work for an agency whose clients include big 5 automakers. Since last April I've been assigned to do maintenance and feature requests on a JS-heavy mobile/tablet build-and-price web app for one auto manufacturer.

The codebase itself seems to originate in 2009; there's some kinds of controllers, but no consistent model to speak of (just lots of service calls, with JSON/data markup stored in and retrieved from the DOM). It has its own not-jQuery DOM manipulation function(s) which, of course, are more poorly conceived and missing significant chunks of the utility of jQuery, and the codebase largely never rises to a level of abstraction above DOM manipulation.

Because bad code is a bit like abuse -- there's a cycle of transmission -- I tend to write pretty bad code for this project. If nothing else, even when I'm writing good-ish code I'm still writing at the DOM manipulation level, which isn't so good. And that's the high point I can aspire to without taking on a significant rewrite (assuming anybody would pay for it).

So, day to day, when I'm not reading terrible code, I'm writing it. Good times.

(Also: not to say that I've never written terrible greenfield code... I have, and some of my never-show-this-to-anyone newbie C code may still be in production as part of an not super popular but nevertheless enduring e-commerce product. Sometimes you start the cycle. :/ )

</comment><comment><USER23> There are two types of emotions I can feel when opening code greater than a year old:

1\. I get excited because I look at my shit code and realize how much better I've gotten SINCE I wrote this piece of dog turd.

2\. I get excited because "Past Me" was clear, documented well, added something that I wasn't expecting, and did an all around fantastic job when I wrote it. Past Me was fucking awesome.

The point is that I'm never ashamed. I'm learning.

</comment><comment><USER24> Yes! Please refer to the image at [1]. It's also on the cover of the introduction of 'Clean Code' [2]. Everyone here has some code that they are not proud of. You should see it as a sign that you've learned from the experience of designing and developing it.

Want to steadily improve on non-production code? Try codewars.com: you're asked to solve 5-30min coding exercises and after can immediately browse the solutions of your peers. The short feedback loop helps you understand how you could've improved while the code is still fresh in your head.

[1] [http://www.osnews.com/story/19266/WTFs_m](http://www.osnews.com/story/19266/WTFs_m)

[2] [http://www.amazon.com/Clean-Code-Handbook-Software- Craftsman...](http://www.amazon.com/Clean-Code-Handbook-Software- Craftsmanship/dp/0132350882)

</comment><comment><USER25> I have code in production that I wouldn't be happy to have to share. I have some that I'm a little proud of. But none of that matters compared to my feelings about whether it's useful and usable. I'm still working on making something I can be proud of in that way.

A few weeks ago someone posted about what it's like to work on the Wolfram Alpha codebase, and described it as a pile of mud. I've read that parts of the Google search codebase could use some rewrites (and a long time ago someone converted it from Larry or Sergey's crappy java code to some nicer python). But noone but the people working on them see that, and while some folks who come and go might mutter under their breath as they sort through what some code might be doing, their opinions pale in comparison to that of the people using those systems every day.

</comment><comment><USER26> All of it. Right now I am looking at an SQL&perl mutant I wrote and wondering what I was smoking at the time.

<comment><USER27> I always try to indicate the model in the comments

</comment></comment><comment><USER28> Oh yes, I'm actually in the middle of cleaning up a huge chunk of it. Managed to strip 1200 LOC yesterday, going for another 1k at least today. Then a long series of commits, a new deployment and the next time I, or anyone else, has to look at this, I won't feel quite as guilty.

That said? It is an incredibly rare luxury in my career thus far, to get these sorts of organizational breaks. I recently checked the code of the first website I ever built - I was learning on the job and asked to convert our flash only website to HTML/CSS.

To my horror, every line of my code was still in place. Every dumb mistake, every awkwardly shoved pixel, every dubious form. Everything. I actually found a couple potentially serious bugs & emailed my former employer to report them. Bleh.

</comment><comment><USER29> I don't think I have any ugly code but I have built insecure apps and sites before. I've asked myself "what if someone does X, could they steal data?" numerous times after a project is out of my hands.

Similar question: How much damage could you do (or data could be stolen) by hacking sites / apps that you built in the past?

My former boss, who was also a software developer, frequently told me that moving to the next project was more important than doing a thorough security audit of large public sites we built. It wasn't my company on the line, so I had no choice.

Also, many of the projects I've built have been proof-of-concepts in my mind that have been rushed into production. Those projects have likely not scaled too well...

</comment><comment><USER30> [https://github.com/kennethrapp/phasher](https://github.com/kennethrapp/phasher)

At least one person is possibly using this in production to match images in a catalog. He contacted me about its tendencies to give false positives (because it's optimized for speed and not necessarily accuracy) and I had to admit that I literally do not understand the math or image algorithms well enough to make it do what he wanted.\

Also most of the code I get paid for is javascript hacks for Business Catalyst sites, which are usually ugly, wicked little monsters whose only redeeming quality is they _work_.

</comment><comment><USER31> I have only written very few pieces of code which I'm not ashamed of right now. A few functions here and there which make me smile when I read them.

But despite that I have the feeling that I usually improve the code I touch. I don't know if that's a good thing, as it means that I usually learn from mistakes I had to make by myself, but at least it's learning, right?

I try to minimize the amount of code I write and don't actually learn on. If that code should become the majority I know that I'm doing it wrong.

I'm coding for only 15 years now (10 years full-time), but I hope that I'll never stop to be ashamed of my code.

</comment><comment><USER32> This was the first Rails app I ever built: [http://projects.propublica.org/bailout/](http://projects.propublica.org/bailout/)

It looks great due to my colleagues actually applying good design principles to it, but the backend is definitely horrible, thanks to me. A contractor helped me with the admin interface but when I left, instead of paginating through the many data entries, it just loads everything up, resulting in a 1 minute delay for any data entry/updating/listing action. I think that was all overhauled by much better devs after I moved on.

</comment><comment><USER33> Not code, but a 2-years-not-upgraded public facing web server.

</comment><comment><USER34> Yeah, a lot of my code that's not a personal project.

I had a lot of code left in my hands when I started the company I'm at, it was awful, and unfortunately I've not got the time to go through and rework it yet. One day though. So everything up until now has just been bolted on and it comes back to bite me at least once a month.

I've started using Laravel now and I feel much more comfortable knowing that it's keeping me on the straight and narrow.

<comment><USER28> "One day though"

I say that to myself all the time.

</comment></comment><comment><USER35> Yes.

My first big web application has absolutely no unit tests. None. Why? Well, when I first learned to code I was taking the Udacity cs253 course, in which there was no instruction on unittesting in Python, so several of my projects shortly thereafter contained no unittesting. I was actually quite proud of these projects until my brother asked if I had any tests ... "what is a test?" ... his face was priceless.

</comment><comment><USER36> Lately the only thing I've been feeling ashamed of is the amount of code I DIDN'T write. I think this is mostly because I fear feeling ashamed of my code after it's running or, even worse, after it's public. I don't know...

I'm the epithome of analysis paralysis. Being ashamed of my own code is my next milestone. :)

</comment><comment><USER37> Yes, actually the better part of the system I'm working on right now. My very own code looks extremely bad to me since I'm continuously learning. Isn't that a normal part of trying to get better? I wrote some really evil stuff already, but I try to get better at least...

</comment><comment><USER38> I worked on some mobile UI stuff which had a couple alignment issues which I didn't get to fix before I left. Now that it's in production, that's all I see as an end user of my own product.

It's nothing disastrous, but seeing it there all the time kills me a little everyday.

</comment><comment><USER39> I find that by calling it "legacy code", it helps to feel less ashamed. In general, I would be worried if I did NOT have production code that I felt could be simpler, faster, or more stable. Maybe just a side-effect of becoming a better developer.

</comment><comment><USER40> I'm not only ashamed of it, I apologized to the programmers that had to come after and maintain it. Regardless, I don't regret it--it helped me get where I am and ironically, it works well and is quite fast. Too bad no one can figure it out.

</comment><comment><USER41> Oh yeah. I assume everyone does.

</comment><comment><USER42> I try to post my shameful code to github in the hopes that I'll do my best with every new project I start. I've only just started it, but it's helped with my thinking process.

</comment><comment><USER43> Yep. But that was done on a time crunch or had to get it done times. So, I make note of things as I write such code wishing to fix them one day :D

</comment><comment><USER44> I can imagine this thread become pretty long... btw definitely.

</comment><comment><USER45> I'm ashamed of half of the code I write

</comment><comment><USER46> Lots of Forrst

</comment><comment><USER47> All of it.

</comment><comment><USER48> always

</comment><comment><USER49> All the code I've written at my current job is deployed to an audience of something like 35 million. As of now, I haven't been made aware of any bugs reported against it. It's formatted nicely. Things are named well. I dunno. How should I best try to fit in with the predictable _mea culpa_ torrent this thread is sure to become? Oh, I must not be learning anything. That's the accompanying natural law, right?

My point: It doesn't make sense to say "of _course_ my code is shit" unless you also have in mind parameters for what would make 'good' code. My standard is simple at this stage: Does it work and not annoy users? Can my team read it? Then I'm not giving it further thought, positive or negative.</comment>


Hacker News All-Time Top Links   - <AUTHOR> http://lab.varun.io/hn_all_time_top_links/ <comment><USER3> When I wrote "Don't Fly During Ramadan" (#3 post), I anticipated it'd get a few hundred hits from my friends if I was lucky. I almost didn't even post it to HackerNews, until a friend told me I should.

....Boy was I wrong.

That aside, it's sad how many of the top posts are negative in some way, even if you exclude the death announcements. The exact count depends on whether you consider things like PG banning SOPA companies to be "negative" news, but only a small handful of the top 30 links are truly positive (eg, Light Table).

It's also interesting how many (or few) of the posts are technical in their nature. I believe I count two that are completely non-technical (mine, and the other one about the TSA), and three or four that are about software that people have built (Meteor, etc.).

The rest are "in the middle" \- mostly about people important to the tech community (Dennis Ritchie, Aaron Swartz, etc.), or about political issues related to technology, but not talking about the technology itself in any depth.

This pattern continues even if you look beyond the top 30.

I wonder if this has changed over time as the HN community has grown - <USER2> in the morning I'll try filtering out posts less than X days old and see how that affects the top results.

<comment><USER4> Well, interestingly enough, not one of the top posts was created earlier than 2 years ago. This is obviously due to karma inflation and slightly akin to saying that Avatar is the top grossing movie of all time (hint: it's not[1] when you adjust for inflation). It'd be cool to see a normalized version of this. If that's even possible...

[1]-[http://boxofficemojo.com/alltime/adjusted.htm](http://boxofficemojo.com/alltime/adjusted.htm)

<comment><USER5> Normalizing per total users or karma has also some side effects. The demographics of HN user base has most probably changed during the past years as well as number of interesting stuff produced also changed. Now there is more submissions more competition.

</comment></comment></comment><comment><AUTHOR> To answer the questions around source and weighting etc:

1: I'm using [https://www.hnsearch.com/api](https://www.hnsearch.com/api), which the page itself seems to indicate is used by HN for certain views.

2: The url is [http://api.thriftdb.com/api.hnsearch.com/items/_search?prett...](http://api.thriftdb.com/api.hnsearch.com/items/_search?pretty_print=true&filter\[queries\]\[\]=url:*&sortby=points%20desc&limit=10&start=0) The API won't let you fetch more than 1000 results in total (using limit and start in tandem).

3: I'm fairly certain the API is fairly new because I never came across any results older than 2010 while fiddling with it. As such the couple of suggestions of weighting by active users might not return the desired balanced result.

4: I'm fetching the results at one go (all 1000) from the API through a cron the runs once every 48 hours.

</comment><comment><USER6> Too bad I didn't get the karma for "Show HN: This up votes itself". I think I at least deserved a few points... :)

<comment><AUTHOR> :) I did notice it when I was making it. In fact the idea to link to google cache popped when I saw that the second highest link was dead, and figured there might be others.

</comment></comment><comment><USER7> 2 of the top 4 posts, and 4 of the top 20 posts are from less than one month ago. A sign of HN's rising popularity.

<comment><USER8> Yes. A "best of all time" should account for "inflation".

</comment></comment><comment><USER9> Very morbid - a lot of endings and very few beginnings. I think that makes a case for the inauspicious HN post not giving a great indicator of start-up success. For example if it were the other way around I would expect to see many many more show HN posts on this list.

<comment><USER10> I think that's not the only way to look at things. Death may be a "bad" thing, but its a natural event that, for the most part, happens on a regular cycle. The fact that these posts get up voted so much isn't an indicator of morbidity, but a sign of how much the community appreciated a person's accomplishments.

Why should Show HN posts dominate this list? By their very nature, things shown in their alpha or beta state should have a select, limited base of fans..they are at the starting point, with no full accomplishments at that point.

By when someone dies, the fact that they merit a news post, and then an HN submission, signifies that they had accomplished very much in life...the up votes are well-deserved and come out of genuine appreciation as much as remorse.

The most up voted obituary posts _should_ be up voted the most, as they, by their nature, are about the most accomplished of our peers, idols, and mentors.

<comment><USER11> "No one wants to die. Even people who want to go to heaven don't want to die to get there. And yet death is the destination we all share. No one has ever escaped it. And that is as it should be, because Death is very likely the single best invention of Life. It is Life's change agent. It clears out the old to make way for the new. Right now the new is you, but someday not too long from now, you will gradually become the old and be cleared away. Sorry to be so dramatic, but it is quite true.

Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma — which is living with the results of other people's thinking. Don't let the noise of others' opinions drown out your own inner voice. And most important, have the courage to follow your heart and intuition. They somehow already know what you truly want to become. Everything else is secondary."

</comment></comment></comment><comment><USER12> I'm not sure how you're collecting this data but any chance you could do (points)/(# of [registered or active] users at the time it was posted)?

<comment><USER13> Yea that would actually give a better dynamic of the number of votes. Right now the top ones are the recent ones which makes sense since the number of active users here is growing.

</comment></comment><comment><USER10> Kind of funny that the user who posted the top ranking link -- the news of Steve Jobs death -- that was just about the only thing he posted:

[https://news.ycombinator.com/submitted?id=patricktomas](https://news.ycombinator.com/submitted?id=patricktomas)

<comment><USER14> It's also rather funny (or sad) that a lot of the posts are about the death of a person/project/business.

<comment><USER15> "If it bleeds, it leads".

</comment></comment></comment><comment><USER16> There's an easier way to find this list.

[https://www.hnsearch.com/search#request/all&q=+&sortby=point...](https://www.hnsearch.com/search#request/all&q=+&sortby=points+desc)

But, it's cool to see old posts :)

</comment><comment><USER17> Static content shouldn't require javascript.

<comment><USER18> Or cookies :(

<comment><USER17> Sure, but cookies are typically the result of using a web framework rather than a design decision.

<comment><USER19> Isn't using a web framework a design decision?

</comment></comment></comment></comment><comment><USER20> Half the posts are less than 1 year old. Wish there was a way to normalize it against active users around the posting date.

It might be interesting to plot the top 4-5 posts per month.

</comment><comment><USER21> For anyone who wants this for themselves, here's a Python script [0] that generates similar output [1]. It doesn't have the nice Google Cache feature from ajani's version, but it does produce static HTML which some may prefer.

[0] [https://gist.github.com/tav/5545779](https://gist.github.com/tav/5545779)

[1] [http://tav.espians.com/temp/hacker-news- top-1000.html](http://tav.espians.com/temp/hacker-news-top-1000.html)

</comment><comment><USER22> I wonder what this would look like if you normalized the number of points to the total number of HN users. There is a huge bias here toward newer stories.

</comment><comment><USER23> This post is broken to me right now. (Monday, 9/9 5am EST)

The URL it points to is: [http://lab.varun.io/hn_all_time_top_links/](http://lab.varun.io/hn_all_time_top_links/)

But the page looks like this in my browser:

{{key + 1}}. {{link.item.title}}

({{link.item.domain}})

{{link.item.points}} points by {{link.item.username}} {{link.item.create_ts | fromNow}} | Google cache | {{link.item.num_comments}} comments on HN

<comment><USER24> As dumb as it sounds this is because the site requires Javascript to load its static content.

</comment></comment><comment><USER25> Whoever did this, I'm a newbie to coding, and I'd want to see the code used to pull this. I'm assuming it wasn't done manually. I've been thinking I'd want to do something similar once I get the hang of it, and to play around with it. "Hall of fame" comments or articles aren't the only ones someone new to HN would be interested in, right? How about, for example, the speed at which an article rises to the top? Or how long it remains at the top? And so on and so forth. You could probably try to draw an analogy to music charts. I don't know. But I'm working on knowing. So if you don't mind sharing, I'd appreciate.

<comment><USER25> Oh yeah, plus I keep hearing about how the quality of HN has dropped. I imagine you could selectively pull stuff from back in the day, apply the same popularity criteria to today, and compare the quality of what comes up. It's small curiosities like this I want to learn how to pull off.

</comment></comment><comment><USER26> Is the score scaled in any way, or just most points?

</comment><comment><USER27> If you like this, you might enjoy my Wayback Letter ([http://waybackletter.com](http://waybackletter.com)) which sends you the best articles from each year of HN every week. Here is last week's issue: [http://eepurl.com/EGWOv](http://eepurl.com/EGWOv).

</comment><comment><USER28> Wow, I'd never read _A Sister 's Eulogy for Steve Jobs_ before.

"His breathing changed. It became severe, deliberate, purposeful. I could feel him counting his steps again, pushing farther than before.

This is what I learned: he was working at this, too. Death didn't happen to Steve, he achieved it." -Mona Simpson

<comment><USER24> Glorify more?

</comment></comment><comment><USER29> How is this calculated? By raw points I should have somewhere around #60, but my top post is not listed. I don't think it's a great post (just me bitching about scores being taken away) so I guess I'm glad, but still curious.

</comment><comment><USER30> Interesting how few of the links are about programming. I expect this is b/c programming is largely about specific technologies, so will appeal to only segments of the readership, whereas Steve Jobs dying has a more universal interest.

</comment><comment><USER31> I do a rolling daily list of the ones that have the most active comments at [http://knowabout.it](http://knowabout.it) in case anyone is interested (it's how this thread caught my attention)

</comment><comment><USER32> Wow, the amount of death posts.

</comment><comment><USER33> It'd be cool to see this adjusted for "vote inflation" \-- the HN population was much smaller 2 years ago than it is now.

</comment><comment><USER34> Not surprised that the most pandering stuff (especially "Don't Fly During Ramadan") is at the top.

</comment><comment><USER35> 18th most popular HN story ever... w00t :)

</comment><comment><USER36> As another poster pointed out down-thread, this list really needs to be adjusted for inflation.

</comment><comment><USER37> Steve Jobs obituary is the #1 post. Hats off to one of the greatest innovators of our lifetime.

</comment><comment><USER38> Which are the most commented posts?

</comment><comment><USER39> It seems dominated by links about people dying and things shutting down.

</comment><comment><USER40> An option to detect and remove broken links could be nice.

</comment><comment><USER41> Unfortunately sad titles prevail.</comment>


Show HN: Hack Reactor students redesign Hacker News for web and mobile - <AUTHOR>

Hey everyone!
I'm currently a student at Hack Reactor.
A previous group, for their Greenfield project, redesigned a web version of Hacker News. Their project can be found here (goosewindmill was the random name of their group):
http://goosewindmill.herokuapp.com/
I am currently working with another group for a Legacy project and we "inherited" the goosewindmill project. Other members of my group are working with the web version. I redesigned the web app for mobile. Here is the link to the app in the Google Play store:
https://play.google.com/store/apps/details?id=com.ionicframework.hnmobile608379&hl=en
We'd love to hear your feedback and improve what we're working on! Thanks in advance. <comment><USER2> Intial thoughts: 2/3rds of the screen are seemingly things I don't care about. It is also not centered which annoys me a bit. Light gray on white is hard to read.

Image for reference of how it looks to me: [http://i.imgur.com/7xzmqxJ.png](http://i.imgur.com/7xzmqxJ.png)

</comment><comment><USER3> Pretty cool project! On safari 8.0.6, the "HN Feed" h1 is hidden on a 2880*1800 resolution (MacBookPro Retina 15''). Keep working hard, maybe I will use it :). Did you open-sourced the project? Many of us would be interested to look at it.

<comment><AUTHOR> Yes! Both the web and mobile versions are in this repo:

[https://github.com/michaelcheng429/Goose- Windmill](https://github.com/michaelcheng429/Goose-Windmill)

Thanks for the input!

<comment><USER3> Cool! Thanks

</comment></comment></comment><comment><USER4> looks sharp!</comment>


Scientists Have Made a Quantum Encryptor 1,000 Times Smaller Than Before - <AUTHOR> https://www.sciencealert.com/scientists-have-made-a-quantum-chip-that-s-1-000-times-smaller-than-before/ <comment><USER2> If a PKI key exchange over classical layers is still required, this device is probably functions solely as a show piece. It's amazingly compact, so that is a good place to make a mark in the industry.

A classical binary en/decryptor chip that destroys the private key on first failed use would do essentially the same thing at considerably less cost.

Now that they've made this so small, does it mean current quantum chips are soon to be obsolete? What about the refrigeration?</comment>


Lowe's Landing Page - <AUTHOR> http://www.lowes.com/

<comment><USER2> Shopper prioritization application ([http://www.akamai.com/dl/feature_sheets/Akamai_Shopper_Prior...](http://www.akamai.com/dl/feature_sheets/Akamai_Shopper_Prioritization.pdf)).

Linked to a page which displays one of four random mp4s which refreshes once every thirty seconds via a meta refresh.

Magic!

</comment><comment><USER3> That homepage makes me feel sick.

Is that why you posted it?

Because the design is terrible?</comment>


Robert Morris, Pioneer in Computer Security, Dies at 78 - <AUTHOR> http://www.nytimes.com/2011/06/30/technology/30morris.html <comment><USER2> What sad news.

When I was doing my doctorate in security I used to attend or give papers at the IEEE Computer Security Foundations Workshop (<http://www.ieee- security.org/CSFWweb/>) which was held in a lovely old hotel in Franconia, NH. This was a really small gathering of people deeply involved the theory of securing computers.

Bob Morris and his wife Anne used to attend each year. It was unusual for people to bring their loved ones to this gathering and having the two of them there gave a certain holiday like atmosphere to the whole affair.

He was a gentleman and very kind to me as a young graduate student and I remember well playing games in the hotel grounds with him and Anne. At the time he was Chief Scientist at the NSA and the Rainbow books had been produced under his gaze. But he was humble, approachable and helpful.

Condolences to rtm.

</comment><comment><USER3> For those who don't know, this is rtm's (YC partner Robert T. Morris) father. My condolences to him.

</comment><comment><USER4> Until I read the age at the end of the title I thought it was the younger RM that had died, esp. with the black band at the top of HN.

What a pity, that's sad news to wake up to.

Condolences to Robert.

[http://en.wikipedia.org/wiki/Robert_Morris_%28cryptographer%...](http://en.wikipedia.org/wiki/Robert_Morris_%28cryptographer%29)

<comment><USER5> It sucks to reach up and hit refresh on HN and see that black bar jump out at you and know we've lost someone great.

 _Never underestimate the attention, risk, money and time that an opponent will put into reading traffic._

 _Rule 1 of cryptanalysis: check for plaintext._

 _The three golden rules to ensure computer security are: do not own a computer; do not power it on; and do not use it._

 _\--Robert Morris_

I'm glad he powered on and used his.

<comment><USER6> I love those rules. RIP Robert Morris.

<comment><USER7> Unknowingly, all of us who use technology have been touched by your father's work. Thank you very much for his contributions to the world.

</comment></comment></comment></comment><comment><USER8> Cliff Stoll has a nice account of meeting Robert Morris at the NSA in chapter 45 of "The Cuckoo's Egg".

<http://users.tmok.com/~pazzi/cuckoo_egg.pdf> <http://en.wikipedia.org/wiki/The_Cuckoo%27s_Egg_(book)>

<comment><USER9> Thanks for posting that, this was the passage I thought of immediately upon seeing the announcement, and my introduction to who Morris was and why he was so important.

Condolences to rtm. A mighty oak has fallen.

<comment><USER10> Wanted to say the same thing, I just finished the book a week ago, never heard of him before and still I feel strangely affected by his death. Condolences to his family and firends.

</comment></comment></comment><comment><USER11> Condolences to rtm and family.

It's a poignant reminder of just how young our field is that we are mourning the loss of some true early pioneers. Imagine if you were a physicist just learning of the passing of Newton? It's also a reminder to value the experience and wisdom of those who are still here with us...

</comment><comment><USER12> RTM,

I don't know what to say. I'm just one of many strangers who knew of your dad, and appreciated his work, but never had the pleasure of actually meeting him. I'm sure there are a lot of strangers like me who feel uncomfortable saying anything more than offering condolences but also feel offering condolences is not enough. We wish there was more we could do. I hope by stating this difficulty for strangers, you are reminded of how amazingly lucky and blessed you are to have known him. You will always remember your loss, but it equally important to also remember your luck. I hope the warm thought of counting your blessings will help you and your family through the troubled times.

</comment><comment><USER13> Condolences to rtm and the rest of his family. Here is a link to one of the nicest stories about Robert Morris, written by Dennis Ritchie, his friend and former colleague:

<http://cm.bell-labs.com/cm/cs/who/dmr/crypt.html>

<comment><USER14> thanks for this. My condolences to his family.

</comment></comment><comment><USER15> I was fortunate to meet rtm a couple months ago; a very nice (and smart, insightful, etc.) guy. My condolences on the loss of his father.

</comment><comment><USER16> It's important to pause now and then to recall what a glorious time we live in, where we are alive at the same moment as so many other great human beings. Some in technology. Some in humanitarian works. Some in music. Even if they're near the end of their time here, that you and I existed on this ball of rock at the same time they walked it is pretty fantastic.

My best to Mr. Morris and his family and friends.

</comment><comment><USER17> Over on Slashdot is "a story I've kept secret for 30 years" about Robert Morris harvesting logins at Bell Labs. [http://it.slashdot.org/comments.pl?sid=2285212&cid=36628...](http://it.slashdot.org/comments.pl?sid=2285212&cid=36628248)

</comment><comment><USER18> A giant. Condolences.

</comment><comment><USER19> From wikipedia: Robert Morris was the author of crypt the UNIX encryption tool. ViM uses (to the best of my knowledge) crypt when called with -x.

</comment><comment><USER20> Condolences to rtm.

</comment><comment><USER21> Anyone would be lucky to live as full, productive, and long a life. Condolences to Rtm and his family.

</comment><comment><USER22> I knew someone had died before I even started scanning the stories, because of the black bar up top.

My condolences to his family. He was quite an accomplished computer scientist.

</comment><comment><USER23> Condolences. The world is better for having had him, and worse for having lost him.

</comment><comment><USER24> So that is what the black bar is about, I was afraid it was a childish pun on Google's redesign :-S and now I feel bad for thinking that.

<comment><USER25> Don't feel too bad, that's what I thought too.

</comment></comment><comment><USER26> RTM and his controversial (for that time) Internet worm were key influences during my early years with computers and computer networks. My condolences to him and the rest of the Morris family.

</comment><comment><USER27> My condolences to the man and those he was close to. I hope he died comfortably and untroubled.

</comment><comment><USER28> My condolences also to rtm. I assume this is why the top bar of HN has a black border-top?

</comment><comment><USER29> Sad news, my sincere condolences to rtm and his family.

</comment><comment><USER30> I feel terrible that I think of his son first every time I see something about him. May he rest in peace.

</comment><comment><USER31> This must explain the Black added to the HN top banner (or someone is spoofing Google+). Condolences, rtm.

</comment><comment><USER32> RIP. Condolences to family and friends.

</comment><comment><USER33> I don't think anything I write can relieve rtm and his family's pain now. All I can hope is they take comfort in the thought that the pain will fade and what will remain are teh memories of an extraordinary man, and that nothing will take those away.

</comment><comment><USER34> Side note, contrast: "Robert Morris, a cryptographer who helped develop the Unix computer operating system" with a 2001 picture of him with his hand on the mouse of a computer running Windows 3.1.

</comment><comment><USER35> A great loss, he will be missed! Condolences to RTM and family.

</comment><comment><USER36> Deeply saddened by the news. Condolences to RTM and family.

</comment><comment><USER37> Very sad news. Condolences to family and friends.

</comment><comment><USER38> So sorry to hear this. RIP, Mr. Morris.

</comment><comment><USER39> Sorry to hear this sad news.

</comment><comment><USER40> My condolences.

</comment><comment><USER41> My condolences

</comment><comment><USER42> RIP Robert Morris. Getting a question from you about my YCom application was one of the highlights of last year.

<comment><USER43> The guy who died is not the YC fellow, but the YC fellow's father.

Condolences to him, anyway.</comment></comment>


Avatar-style S. Korean manned robot takes first baby steps - <AUTHOR> http://phys.org/news/2016-12-avatar-style-korean-robot-baby.html <comment><AUTHOR> I submitted this because a couple of weeks ago, some people (including me) thought this project was a fake: [https://news.ycombinator.com/item?id=13204377](https://news.ycombinator.com/item?id=13204377)

It doesn't seem to be, or if it is, it's an unbelievably good one. As an old Patlabor/Macross fan I'm eagerly watching developments in this space. This seems to be the real deal.</comment>