HN user

Arkadir

102 karma
Posts0
Comments28
View on HN
No posts found.

One of the rats achieved a 53.5% success rate (the author did not specify whether this was over the entire 800 data points or only the last 100).

When tested on 800 data points, a purely random strategy would have a > 53.5% success rate about 2% of the time.

structure = json.loads(json_string)

Not quite. Let's say your JSON data contains the following attribute:

    "access" : [ "view", "edit", "admin" ],
This field should be represented (in the language) as a set of values from an "access-levels" enumeration.

In C#, you'd have the following boilerplate:

    [DataMember(Name = "access")]
    public HashSet<AccessLevels> Access { get; private set; }
In OCaml, it would be:
    access : Access.Set.t ;
The simple "json.loads" solution would return a list of strings instead. What's the Python code for turning it into a set of enumeration values, and failing if one of the values does not match ?

Easy language interoperability as a reason to choose Protobuf over JSON ? Mainstream languages support both JSON and Protobuf equally well, and the others tend to support JSON more often than Protobuf.

Free backwards compatibility ? No. Numbered fields are a good thing, but they only help in the narrow situation where your "breaking change" consists in adding a new, optional piece of data (a situation that JSON handles as well). New required fields ? New representation of old data ? You'll need to write code to handle these cases anyway.

As for the other points, they are a matter of libraries (things that the Protobuf gems support and the JSON gems don't) instead of protocol --- the OCaml-JSON parser I use certainly has benefits #1 (schemas), #3 (less boilerplate) and #4 (validation) from the article.

There is, of course, the matter of bandwidth. I personally believe there are few cases where it is worth sacrificing human-readability over, especially for HTTP-based APIs, and especially for those that are accessed from a browser.

I would recommend gzipped msgpack as an alternative to JSON if reducing the memory footprint is what you want: encoding JSON as msgpack is trivial by design.

Many companies offer production-grade hosting for open source applications. The fact that you are keeping your source closed tells me that you are afraid a competitor (or your clients themselves) could achieve an equivalent quality of hosting for less than they would pay you. To a customer like me, this is bad news.

At RunOrg, we've encountered this a few times with our CORS-only API.

It is against our philosophy to leave users behind only because they are locked in by outdated infrastructure. We still want to support them.

It is against our philosophy to bend the purity of our API to accommodate wrinkles in how outdated infrastructure declines to support standards. There will be no JSONP alternative to CORS in RunOrg.

The solution we propose each and every time is to mount a proxy to our API on the same domain as the site it is used on. Users on modern infrastructure reach us directly at api.runorg.com, users on CORS-hostile infrastructure reach us through the proxy and still get their data (albeit with decreased performance). It's a fairly simple technical solution that leavers our API clean and supports non-CORS modes of access.

The Single Responsibility Principle is like Body Mass Index for code: it's easy to measure (humans have an innate sense of what "reponsibility" means) but outside of extreme situations it is not precise enough to base serious decisions on: there is a huge grey area where people do not agree on what responsibilities are.

It's the same as EDD: it's fairly subjective, but it weeds out the extreme cases.

The benefits of SRP are usually a side-effect of applying more objective rules to the code.

DRY is the primary driving force. It pulls shared responsibilities out of modules and leaves no doubt that those responsibilities were shared. It detects recurring concepts and gives them a representation, thereby increasing coupling.

The second force is to reduce access to private code: out of 100 lines of code in your module, how many actually need to access that private concept on line 42 ? If the answer is "30", then what are those other 70 lines doing in this module ?

Apply both forces to a code base, and the SRP will appear out of nowhere.

The most important rule about the future is that You Aren't Gonna Need It. If the future is known then it is called "requirements". Design is for protecting you from the unknown future: allowing code to be changed in arbitrary, unforeseen ways.

As for coupling and cohesion: if modules are too small, they usually lack cohesion ; if module are too big, they usually have high internal coupling. The sweet spot is in the middle. And you can create low-cohesion, high-coupling modules if you want :-)

RIP TDD 12 years ago

Look at Kent's eight bullet points: half of them are unrelated to the DD part of TDD. Coming from a man who should know the difference between having automated tests for your software and partaking in test-driven design, I must assume that he had a good reason for conflating the two.

Hacker News is not the audience for this post.

Kent Beck is doing damage control.

DHH lives in a developer community that has adopted unit tests to the point of feeling ashamed about untested code---testing is more than just a practice, it's a culture.

Kent Beck lives in a world where the mere existence of unit tests is a champagne-worthy surprise.

There are people out there who are not yet convinced of the benefits of tests, let alone test-first or test-driven design. Kent Beck is a missionary, bringing them the gospel of automated testing. Can you imagine the impact that a piece like DHH's would have on his efforts ?

A developer with an incomplete understanding of software tests, reading a post by a big-shot recognizable name that one would expect (based on the Rails community's love for testing) to be a major proponent of tests, would take it as "Tests are actually a bad idea !"

This is not what DHH said. There is probably no one among us here who would understand it this way, and most experienced folks would just shake their heads at the ongoing back-and-forth and resume their position of "TDD is a tool in my toolbox, and I use it whenever it helps me."

Kent Beck wrote a piece for the lost soul who doesn't know the difference between TDD and automated testing, and who might become confused after reading DHH's opinion. This is no time for subtlety, for paying notice to the differences between testing methodologies, or for polite agreement with at least some parts of the "opposing" piece.

Let David work to bring the Rails community back from the "test all the things!" extremities it might have reached, let Kent work to bring the unenlightened masses out of the "tests are useless!" darkness wherein they have dwelt for so long, and let us accept that if we truly have the capacity to criticize what those two are saying, then they probably weren't talking to us in the first place.

Your link mentions three servers with 384GB RAM and 16 RAID10, and 16 cores each. Not very far from my 128GB 32 core example :)

A few years ago, I came up with a similar proposal, though more specifically aimed at making callbacks easier to manage. It was a simple syntax extension, as opposed to new semantics for the language.

You would write this:

    var! x = expression;
    statement;
    statement;
And JavaScript would parse it as:
    return expression.then(function(x) {
      statement;
      statement;
    });
This is obviously shamelessly pilfered from monadic syntax 'let!' in F#, and my own 'let!' extension in OCaml.

From my understanding, it is not the evaluation of a "future" value that causes a context switch, but rather its use:

- passing it as an argument to an operator or built-in function

- accessing one of its its members

- calling it as a function

The main reason why writing concurrent code in JavaScript is not completely insane (just moderately so) is because of the single-threaded sequential execution model (also known as cooperative threading).

Asynchronous effects need to remain explicit.

Consider a simple example:

    function binary(stack,combine) {
      var a = stack.pop();
      var b = stack.pop();
      stack.push(combine(a,b));
    }
In today's JavaScript, this code executes as an atomic sequence. It turns [..,a,b] into [..,combine(a,b)] and there is no way for another piece of code to step on this function's toes and break this invariant.

What if "combine()" suddenly became asynchronous ? Either because its operation itself is asynchronous, or the evaluation of "a" or "b" involved an asynchronous operation. This would cause the call to "binary()" to pause in the middle of the function, which would give time to another part of your code to call "binary()" in turn. And now [a,b,c,d] has turned into [a + b, c + d] instead of [a, b + c + d], and you'll have a jolly good old time debugging tht.

Make asynchronous context switches implicit and you start needing locking primitives. Everywhere, including third party libraries that "used to work". Because every single line of code(except plain assignment) could be a ticking async-bomb that will pause the current function and let other functions wreak havoc on your invariants.

Scary.

Callbacks are ugly, and promises are hardly better, but asynchronous operations NEED to stay explicit. And explicit asynchronous operations are contagious. That's just what they are.

Just make the syntax less right-pyramidal: C#'s async/await syntax is a good candidate, and 'await' is a good way to mark a possible context switch.

I certainly agree that most people who pick NoSQL solutions "for scalability" never add a third server to their cluster.

You can check it here: https://github.com/RunOrg/RunOrg/tree/master/server/cqrsLib I'm sorry for the state of the official site, there will be one soon.

A `Stream` corresponds to change events (I'm not sure these even had an official name in CouchDB).

A `Projection` corresponds to a design document.

The various `View` implementations are optimized for various aspects of CouchDB views, with `MapView` being the literal equivalent of a CouchDB view. Except they can be chained (you can apply a map to a map).

Unlike CouchDB, views are evaluated eagerly, though the `HardStuffCache` (and other planned `Cache` implementations) are evaluated lazily on a per-document basis.

NoSQL discussions always seem to conflate three very different things: storage engines, APIs and architecture. Where do we store the data ? How do we access it ? How do we make sure it scales ?

The "traditional" approach is to use Oracle/SQL Server/MySQL for storage, SQL and/or ORM as an API, and single-server tables-with-relationships as an architecture. Back in the early 2000s, everybody did this. Sure, there were a few performance-minded exceptions that went with sharding or master-slave architectures instead, but those were exceptions.

And single-server architectures tend to behave badly at medium loads. Spend the market rate for a genius DBA, and they still behave badly at high loads. The next step is a 32-core 128GB RAM monstrosity that costs an order of magnitude more than what eight 4-core 16GB servers would cost.

Most NoSQL solutions came with a new architecture. You had the MongoDB flavor of distributed storage, or the BigTable flavor of distributed storage, or the CouchDB flavor of distributed storage, and so on. Properly implemented distributed storage eats high loads for breakfast: just add more servers. This is a good thing.

My issue with the NoSQL movement is that they threw away the baby with the bath water. They threw away the single-server relational architecture, which was a nice change, and they also gave up the old battle-hardened storage engines and the highly expressive SQL language and replaced them with only-recently-experimental engines and ad hoc lean APIs.

It takes time for a storage engine to mature. To have all its performance kinks ironed out and all its bugs smoked out. I still remember the brouhaha around MongoDB persistence guarantees, or the critical data loss bugs in CouchDB.

And the lean APIs just forced back all the querying logic into the application, with all the filtering and the manual indexing and the joins and the approximate but ultimately incorrect implementations of whatever subset of ACID was required at the time. This wasn't an entirely bad thing: it certainly made many developers aware of the performance implications of some joins or transactions. But when you need to write a JOIN or GROUP BY or BEGIN TRANSACTION that you know will scale properly, and there's no API support for it ? Feh.

I'm a huge fan of the CouchDB architecture. Distributed change streams, with checkpointed views and cached reductions. But I have been burned by the CouchDB storage engine (can you say "data corÊ–NÑ %ñXtion" ?) and I see no point in bending knee to the laconic CouchDB API. So I took the CouchDB architecture and reimplemented it with a PostgreSQL back-end. It's _faster_ (don't underestimate the cost of those HTTP requests), I have trust that after PostgreSQL's decade-long history all threats to my data are long gone, and I can always whip out an SQL query when I do need it.

It's nice to see so many NoSQL solutions migrating back to an SQL-like API and gaining enough maturity to keep your data safe. In the near future, I expect them to be nothing more than "Architecture in a box" solutions for when you don't want to implement specific architectures in SQL. And I expect more and more "architecture plugins" to become available: with a library, turn a herd of SQL databases into a distributed architecture of type X.

Most of the reactions both here and on LinkedIn seem to miss a crucial fact: this story happened in Belgium.

In Belgium, employees must be notified in advance before they are let go: you send them a letter stating "your contract will end in 12 weeks", they keep working for you for 12 weeks, and then they leave.

Employees are usually expected to pass on their knowledge to other team members and wrap up their current projects before they leave. And they are getting paid for it.

This is not the story of a CEO who fired out an employee on the spot, then tried to get back in touch later because they found out they still needed him.

This is the story of a CEO who told an employee that they would be let go three months from now, then asked them to help after hours---something that they had done previously---only to find that they were not motivated enough to do it anymore.

And this is what makes the story interesting: it's not a ridiculous caricature that you can point and laugh at ; it has all the real-life ingredients that you can easily find in the average company.

- Employers and employees who assume that "professionalism" means volunteering to work beyond the scope of a work contract.

- Employers who forget that loyalty is an essential factor in the motivation of many employees.

- A CEO who made the tradeoff of not having a dedicated 24/7 support team, and whines when the inevitable outage happens and there is no one to handle it.

I believe the original author herself said it best: "So your best bet is to hire people who share your passion, willing to 'volunteer' on such occasions."

No one is passionate about staying long hours to fix a production server. But they might be passionate about building a product that can make them proud. Their product. And once they feel it's not their product anymore, the passion is gone, and they'll be home by 7pm with their cell phone turned off.