Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
A principled approach to system design and programming (urbit.org)
182 points by yosoyubik on March 19, 2020 | hide | past | favorite | 57 comments


Urbit is a cool idea and I resonate with its motivation but I fundamentally disagree with its execution.

The fact that we have bad platforms does not imply that we have bad systems or bad protocols. I think the rotten core of the bad-ness of the internet is commercial infrastructure. It's prohibitively expensive for users to truly own the most basic commodities of internet infrastructure, i.e names (DNS), numbers (IP) and peers (BGP). These commodities would enable users to make the most of the protocols that we already have.

There are already many projects that try to combat the commercialization of the internet by adding new systems and protocols (Solid, IPFS and ActivityPub come to mind). Users shouldn't only be free within the confines of these systems - they should be first class nodes on THE network.

The spirit of Urbit is spot on but it's at the wrong layer of the OSI model. I have much more faith in people who are trying to operate public networks (i.e NYCMesh) and would like to see more projects that aim to de-centralize the basic commodities of the internet.


> It's prohibitively expensive for users to truly own the most basic commodities of internet infrastructure, i.e names (DNS), numbers (IP) and peers (BGP). These commodities would enable users to make the most of the protocols that we already have

If these costs are inherent to the operation of these systems and protocols, and arguably many of them are, then it sounds like Urbit is on the right track.


Is it economically viable to have all this infrastructure without commercial interests?


Much of the physical and logical infrastructure was heavily subsidized or outright paid for with public funds. One could argue that it was never economically viable, but forced to appear that way.

Internet infrastructure is not unique in this respect.

The idea that markets optimizing for profitability naturally tackle hard problems requiring immense up-front costs is an attractive lie.


And so is the idea that public or government can maintain a good quality infra (at scale, everything works in a small community). At least where I live the public company provides the worst possible services and all improvement has come from the private sector competition.


thats why usually the best approach is public owned and private leased..


One can look at existing mesh networks and see that it is indeed possible to have infrastructure running without commercial interests. Guifi is probably the biggest and most successful example of this. Guifi is mostly volunteer driven but also have commercial actors operating on it, helping people getting setup to Guifi for a fee. So the infrastructure is open but it does have for-profit actors helping maintain and create new nodes too.


> Everything should be CQRS.

For systems, this is going to be painful: you want to be able to do things like "compare-and-swap" which are already an action and a query bundled in one. You need this for 2 reasons:

   - consistency: you want the query and the update to be done in one go. atomically
   - performance: doing it like this is faster as 
     you're combining 2 things in one call, iso 2.


That was my immediate thought as well - CQRS doesn't fit well with the "everything should be deterministic" principle, unless they have a very different definition of CQRS than what is in common use.

In theory, it is possible to do "synchronous CQRS", where the update to your write-side is done in a single transaction with the update to the read-side, but that's not how I've ever done it in my (Scala/Postgres based) CQRS app experience.


Compare and Swap is a command. E.g. Optimistic Concurrency.


Consistency and determinism are orthogonal. Just because updates are eventually consistent, doesn't mean they exhibit any sort of indeterminism.


> For example, the conventional name for a variable of type path is pax. You should have a good reason for using any other name for a path.

Ah yes, let's appropriate the Latin word for "peace" and demand everyone use it for paths.

An utterly fanciful take, this. Must be nice to have the time to cultivate and commemorate such a detailed set of principles.


I missed that this was urbit, but as soon as I read your post, I said hm, urbit?


It’s quite difficult to tell if this is genuine or satire. I really hope it’s the latter though.


>The Law of Leaky Abstractions is a lie; abstract airtightly.

Man, when I read things like this about "design principles" I know I'm getting into a bag of hand wavy opinions with no rigid theoretical proofs. That's fine because not everything is covered. But the minute you say something that is outright false I know you're worthless.

All abstractions leak. I cannot make an abstraction perform better than what's underneath. The performance leaks through every single time. An abstraction will always only be worse performing then what's underneath simply due to the cost overhead of the abstraction itself.

Rust is a great example. It has something called zero cost abstractions, meaning that the abstractions that Rust uses are more complicated than normal in order to maintain the zero performance cost. That is leakage. There is always a cost.


> All abstractions leak. I cannot make an abstraction perform better than what's underneath

Performance is not within the scope of abstraction. This is a category error.

Also, your claim is incorrect as a general statement. The type structure of a given program is an abstraction of its value flow, but without that abstraction a compiler would not be able to optimize it nearly as well. A typed program will always be faster than an equivalent untyped program.

Creating an abstraction may also allow you to add a transparent memoization layer, which not only can make your program orders of magnitude faster, it can make previously unsolvable problems solvable (see how PEGs handle left recursion).


> > All abstractions leak. I cannot make an abstraction perform better than what's underneath

> Performance is not within the scope of abstraction. This is a category error.

It's not within the scope of abstraction... until it is. When that beautiful abstraction does exactly what you need, but does it too slowly to be usable in your circumstances, performance is not separable from the abstraction. (I mean, yes, performance is part of the implementation of the abstraction, not the abstraction itself. But the moment I have to care about the implementation of an abstraction rather than the abstraction itself, the abstraction has leaked. Performance is absolutely one of the ways that this can happen.)

And, as a concrete example, the STL went to great lengths to specify the performance of their abstractions.


> But the moment I have to care about the implementation of an abstraction rather than the abstraction itself, the abstraction has leaked.

No, that's not what leaky abstraction means. If an abstraction specifies its performance properties, but those properties depend on context that isn't captured by or enforced by the abstraction, that's a leak.

To use your example of the STL, if you can allocate a fast constant-time abstraction at a misaligned address, thus causing it to violate its own performance specification, that's a leaky abstraction.

Having the wrong performance characteristics simply means it's the wrong abstraction, not that the abstraction is leaky.


I think you're a bit too narrow in your definition. If an abstraction specifies properties, but I have to care about more than those properties to be able to use the abstraction, then the abstraction leaked.

Performance can be one of those things I have to care about.


When GMail was launched, everybody was amazed of how fast it ran.

The way it was optimized for IE6 was that the team decompiled it and understood how the JavaScript engine and the garbage collector were working underneath and got around them. The team went out their way to optimize for the large number of emails that are used insided Google.

The project of course was a huge success.


When DOOM game came out everybody was amazed as well. Same with windows and osx. Many things about computers were understood during this time and were huge successes. But did you know that the fourier wave hit quantum apex and therefore logic was inescapable. But what's most amazing is how irrelevant your post is to what I'm talking about.


If I understand, xiphias2 is agreeing with you - that Gmail went "behind" the abstractions, and was the better for it. That is, when you pushed the abstractions as far as GMail needed to, the abstractions leaked.


This entire Urbit thing has the vibe of a Boards of Canada music video. I love it even if I don't quite understand it (which I bet no one does, including their investors).


I agree with some of this on some level, but this article is a big list of extremely abstract concepts that it doesn't explain how to achieve. It definitely doesn't explain how to achieve them in a practical way in general programs with minimal hits to speed and latency, etc. etc. It's like someone saying 'keep it simple stupid'. Great, tell me how to simplify.


IKR?

> (Almost) Everything should be pubsub?

Gee thanks, I'll just go make everything pubsub. Except the things I shouldn't. The distinction will be clear I'm sure.

I'm not saying I disagree with all their points. But without examples it's not clear what they even mean, let alone how to apply them.


Completely unrelated but I fell in love with the image compression, black and white + high dither.



Cool it looks like they're using a plugin based on https://github.com/hbldh/hitherdither to do the dithering


Same here - does anyone know which tool produces output like this?


Not sure if it is what was used, but there is a Bill Atkinson dither for HTML canvas here https://ipenburg.home.xs4all.nl/atkinsome/ and a mac version here https://www.tinrocket.com/content/hyperdither/#history


Nice one! Found the source for the first linked page:

https://github.com/ipenburg/Atkinsome

The script is about 8 years old and depends on MooTools, but shouldn't be too hard to modernize it.

Here's a newer implementation of the Atkinson dither algorithm on Node.js.

https://github.com/rtrvrtg/node-atkinson-dither

Edit: This dither library includes Atkinson:

https://github.com/danielepiccone/ditherjs



But, this is not discussion at all--it's a doubling-down on various points of the manifesto. "Maybe you didn't catch my subtly brilliant note about tab spacing; now read a further two paragraphs of microdosed wisdom on the matter and you may become enlightened."


Not sure some of these precepts are possible:

> A.2 Everything should be CQRS.

Some things require strong consistency, even at the UI level. Eventual consistency is probably the right default though. Unfortunately, we still don't have good abstractions at the language level to manage this.

> A.4 A subscriber shouldn't affect a publisher.

Literally impossible. Either a subscriber is polling the publisher, or the subscriber registers with the publisher to receive updates. Either way, the subscriber is inducing state transitions in the publisher. What other possible meaning can be assigned to "affect" in order to make this precept valid?

> D.1 Academia has many genuinely smart and interesting ideas, but always remember that pursuing them whole-heartedly will never result in a useful product.

Yeah, not at all sure that's correct. Lots of academic research is funded by commercial interests. In fact, currently one of the biggest corporations in the world at this time (Google) started as academic research.

> D.2 Those people (academics) wasted a lot of time finding the right answer, but now that they've done it, you must exploit it.

Spent a lot of time. "Wasted" implies there was a better way.

> D.5 Practice tells you that things are good or bad; theory tells you why. Never use theory to design a good system from scratch; only practice can tell whether the system is good.

I don't think this is coherent. If theory tells you why a system is bad, then that immediately entails what a good system should look like.

What I think typically happens, is that a system designed by theory is so good at solving the problems it was designed for, that it's then applied to similar-but-not-quite-the-same-problems, and thus it becomes inadequate again.


A.4: I think you're a bit too literal here. A subscriber shouldn't affect the publisher other than by subscribing, and by the tiny bit of time it takes to be called.

Why those two exceptions? Because those two exceptions are mandatory in the publish/subscribe model. You can't escape them and do publish/subscribe. But the claim is, a subscriber shouldn't do anything beyond that, and that is actually a useful and important claim.

A subscriber shouldn't send data back to the publisher via the return value of a subscribed message, still less via reference parameters. A subscriber shouldn't affect the publisher's performance by taking too long to handle one of the messages. There may be other ways as well.

D.5: There's a difference between theory and practice. If the system is good in theory but bad in practice, then theory will not tell you so. Only practice can tell you.

You can, based on that practice, create an improved theory. That theory will tell you what a good system should look like... in theory. In practice, such a system may still be bad, just in a different way from the previous system. Again, you need practice to tell you this, not theory.


I've found this list quite dogmatic. I would like to see them wrestle with the business side of my company at the meetings.


42: Use typed and safe languages

https://github.com/urbit


This is a set of very strong and detailed opinions about design. Do they have any street cred to back it up?


> The Law of Leaky Abstractions is a lie; abstract airtightly

This one... Our entire computing world is a pie of terrible abstractions held together by duct-tape and billions of man-hours spent working fixing those leaks.


This one is wrong. The Law of Leaky Abstractions is in fact not a lie. All abstractions actually do leak.

People take that as an excuse to be sloppy, which it isn't. You should do the best you can to make non-leaky abstractions. When you have done so, you should look for the leaks, and do your best to plug them. After that, you should do so again. You should really try to make it as solid as you can.

But it will still leak. It will be less garbage than much of what we have to build on, because of the effort you put into making it better. But it will still leak.


I agree. I much prefer the more general “All models are wrong, but some are useful" to the “Law”.


1. Too many principles equals no principles, for example: Don't exceed 10 principles.

2. Simplicity and Unity must be parallel. Pure piping systems meet this principle.

3. Principle should be specific and executable.

4. Principle should not affect flexibility.

5. Principle should be consistent with mainstream modern industrial production principles.

https://github.com/linpengcheng/PurefunctionPipelineDataflow


Your list is inconsistent. Principles necessarily constrain flexibility. For instance, principle 5 means I can't create a system that isn't consistent with mainstream modern industrial production principles.


Loved the language - "Each timeless data structure is a brick in the foundation of digital civilization."


> Deterministic beats heuristic.

> Heuristics are evil and should only be used where determinism is infeasible, such as in cache reclamation

are these two things really in conflict? they seem orthogonal, but maybe i’m missing something...


Yes that's most likely a misunderstanding there - most heuristics I've used were very much deterministic. Non-determinism is evil, that I would agree - but heuristics aren't. As long as you understand they are heuristics and might fail/lead to suboptimal results.


Most internet APIs use timeouts. That's as nondeterministic as it comes.


Networks are non-deterministic by nature... Just like disk IO, but on networks it's in an entirely new level.


Which is why the first rule of distributed systems is "don't distribute your system"


Yes, the network is non-deterministic. The point of a reliable protocol is to make it the outcome deterministic despite any underlying non-determinism. So why include timeouts in your protocol which just adds back the non-determinism?


The article mentions a `duct` abstraction several times but I've never heard of this nor been able to find anything relevant through some searching. Does someone have some insights on it?


From the discussion [1]: "A duct is akin to a first-class call stack".

For a more detailed explanation, you can look up the type definition [2] in Arvo (Urbit's Kernel) and here [3] you can find an example of how the stack looks when setting up a timer.

[1] https://urbit.org/blog/precepts-discussion/

[2] https://urbit.org/docs/tutorials/arvo/arvo/#duct

[3] https://urbit.org/docs/tutorials/arvo/move-trace/


The lengths devs go to become priests..


To cache, to seek, to find, but not to yield...


Do you mean in terms of taking extra discipline on themselves (which might seem unnecessary to outsiders), or in terms of preaching what they think is right?


I think he means in terms of defining and controlling orthodoxy.


In terms of feeding their own sense of importance.

Opinion is mine. Apologies to GP if I twisted their metaphor into something much less charitable.




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

Search: