<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hacker News: MrJohz</title><link>https://news.ycombinator.com/user?id=MrJohz</link><description>Hacker News RSS</description><docs>https://hnrss.org/</docs><generator>hnrss v2.1.1</generator><lastBuildDate>Sat, 23 May 2026 01:48:16 +0000</lastBuildDate><atom:link href="https://hnrss.org/user?id=MrJohz" rel="self" type="application/rss+xml"></atom:link><item><title><![CDATA[New comment by MrJohz in "Deno 2.8"]]></title><description><![CDATA[
<p>I think the biggest issue with Deno is that it fixes real issues but in the wrong way.<p>Take the sandboxing stuff.  In theory, you have always been able to sandbox your applications.  There are so many tools that let you limit what domains an application can access or restrict access to the file system.  This doesn't need to be handled at the language/runtime level.  It's just that people were lazy before, and they will continue to be lazy afterwards by running Deno applications with fewer than the minimum set of restrictions because that's easier.<p>The more complete way of solving the problem would have been capabilities.  Rather than sandboxing the whole application, you instead sandbox each individual function.  By default a function can make no requests, access no files, execute nothing, etc.  But while the application is running, you can pass individual functions a token that grants them limited access to the filesystem, say.  This means that trusted code is free to do what is necessary, but untrusted code can be very severely limited.  It also significantly reduces what dependencies can do: if you're using something like `lodash` which provides random utilities for iterating over object keys and the like, and suddenly it starts asking for access to the web, then clearly something is wrong, and the runtime can essentially make that impossible.<p>It's also great for things like build scripts, which are a common attack vector right now.  If your runtime enforces that the build script only has access to the files in the project folder, and can't access arbitrary files or run arbitrary commands, then you're in a much safer position than if your build script can do basically anything.<p>This concept has been explored before, but JavaScript is basically ready-made for it.  The language already has everything you need — a runtime that also acts as a sandbox, unforgeable tokens (e.g. `Symbol` or `#private` variables), etc — and you can design an API that makes it easy to use capabilities in a way that enforces the principle of least privilege.  The biggest problem is that there's basically no way to make it backwards compatible with almost anything that works with Node, because you'd need to design all the APIs from scratch.  But one of the great things about Deno at the start was that they did try and build all of the APIs from scratch, and think about new ways of doing things.</p>
]]></description><pubDate>Fri, 22 May 2026 21:55:13 +0000</pubDate><link>https://news.ycombinator.com/item?id=48242178</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=48242178</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=48242178</guid></item><item><title><![CDATA[New comment by MrJohz in "The Emacsification of Software"]]></title><description><![CDATA[
<p>It's usually easier to build something that maintain it for extended periods of time, particularly if that maintenance requires adding new features.</p>
]]></description><pubDate>Wed, 13 May 2026 21:08:36 +0000</pubDate><link>https://news.ycombinator.com/item?id=48127594</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=48127594</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=48127594</guid></item><item><title><![CDATA[New comment by MrJohz in "A Gopher Meets a Crab"]]></title><description><![CDATA[
<p>And yet the examples in this post are of slop code, so clearly they're not <i>that</i> long gone.</p>
]]></description><pubDate>Sun, 03 May 2026 00:47:02 +0000</pubDate><link>https://news.ycombinator.com/item?id=47992097</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47992097</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47992097</guid></item><item><title><![CDATA[New comment by MrJohz in "A Gopher Meets a Crab"]]></title><description><![CDATA[
<p>I mean, it's not that surprising that you'll learn better in a stack you already know well - you know enough there to know what you don't know and need to learn. But if you don't know anything about a language, it will be very difficult for you to sort fact from fiction.</p>
]]></description><pubDate>Sat, 02 May 2026 10:37:26 +0000</pubDate><link>https://news.ycombinator.com/item?id=47985176</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47985176</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47985176</guid></item><item><title><![CDATA[New comment by MrJohz in "A Gopher Meets a Crab"]]></title><description><![CDATA[
<p>It's basically doing the same thing that, say, `return true` might do to indicate a function succeeded, but with more explicit types. However, because it uses `Result`, it can be used with the `try`/question mark operator which can be convenient in some situations.<p>That said, a couple of the examples here feel a bit strange - they're clever things you can do, but they're not necessarily things you often have to do, particularly for a relatively simple task like this. I think the problem with the author's approach is that they can't distinguish between "weird because Rust is weird" and "weird because the LLM generated bad code", because they (understandably) don't have enough experience in what good Rust code looks like.</p>
]]></description><pubDate>Sat, 02 May 2026 08:33:12 +0000</pubDate><link>https://news.ycombinator.com/item?id=47984567</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47984567</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47984567</guid></item><item><title><![CDATA[New comment by MrJohz in "Understand Anything"]]></title><description><![CDATA[
<p>As I understand it, in teaching there's an idea of the "Zone of Proximal Development" (ZPD). Some things you can do without help, other things you can only do if others do it for you, and then in between there's all the stuff that you can do with some amount of assistance. Being in this zone is important for learning, at least in theory.<p>I suspect that's kind of happening here. If you're trying to learn something too abstract or distant from what you currently know, you'll probably use more polished or eli5-y sorts of material, because you don't yet have the skills to understand a more complex version. You're probably not in the ZPD.  But if you can figure some things out by yourself, possibly with some amount of help, then you're in the learning zone and can meaningfully progress.<p>I have similar experiences to you with 3B1B - it's interesting, but I rarely retain anything meaningful after I've finished - and I think it's because he has to explain every part for me to understand what's going on. I'm not in the zone of proximal development because I can't do enough of the work myself. So the end result is an interesting video where someone explains a cool concept to me, but it's not learning because it's not also doing all the foundation work that gets me to the point where I can understand the video for myself.</p>
]]></description><pubDate>Fri, 01 May 2026 21:58:51 +0000</pubDate><link>https://news.ycombinator.com/item?id=47980961</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47980961</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47980961</guid></item><item><title><![CDATA[New comment by MrJohz in "The future of everything is lies, I guess: Where do we go from here?"]]></title><description><![CDATA[
<p>This is true, although I have to say as someone who doesn't own a car, good public transport can avoid most of those issues. I live in a small-ish city (500K - 1M pop, depending on how you count it), and I can get pretty much anywhere I need to without worrying about schedules and certainly without worrying about strikes. The biggest issue is getting out of the city - that's when it's usually more important to worry about schedules, but it's still mostly doable - and occasionally transporting furniture or something like that.<p>On the other hand, the benefits I get from that public transport are incredible - it's cheap, it's always there, it requires minimal logistics in groups (no trying to figure out who goes in what car and needs to be dropped off where at what time), it works regardless of my level of inebriation (admittedly I've not pushed that one to any sort of extreme yet), it's safe enough for children to travel independently (no dropping them off and picking them up), and it's largely accessible for people with difficulties walking or moving about.<p>I think a big part of the issue is that people have tried out poor public transport infrastructure and recognised - often correctly - that their car is way better for them. But good public infrastructure can often be far more convenient than cars, it just requires people to be motivated enough to build and finance it. A neighbour of mine didn't notice his car had been towed for a week because he used public transport so much and so rarely touched his car. When he'd parked his car it was fine, but then they needed to block of the street to do some work somewhere, and he didn't notice they'd confiscated all the cars there. That's the sort of effect that good public transport can have - so comfortable that you can forget you even have a car.</p>
]]></description><pubDate>Thu, 16 Apr 2026 20:43:23 +0000</pubDate><link>https://news.ycombinator.com/item?id=47799233</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47799233</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47799233</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>I'd add jj to that list, tbh. It simplifies a lot of stuff, but in doing so it exposes a lot of those core ideas. That's where I got my list of essential complexity, really - the stuff that comes to the fore when you start using jj.</p>
]]></description><pubDate>Thu, 16 Apr 2026 20:17:07 +0000</pubDate><link>https://news.ycombinator.com/item?id=47798921</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47798921</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47798921</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>Did you mean inherent complexity instead of incidental complexity?<p>I think the inherently complex things in git are (1) the content-accessible object store, snapshots, plus the merkel tree approach to keeping track of commits and parenthood, (2) merges, rebases, and resolving conflicts between two different changes with a common ancestor, (3) possibly syncing commits between different remotes, although I think Git's approach adds accidental complexity to the problem as well.<p>Everything else is a question of the user interface: how you choose to show a commit, how you choose to update the project files, how you choose to let people create new commits, etc. And I think the Git CLI is a poor user interface in a lot of places. There are a lot of features which are really powerful but difficult to use, whereas actually they could be just as powerful but far more intuitive to use.<p>In fairness, this is no slight to the Git developers - they are improving a lot of that interface all of the time. And finding a simpler interface is often a lot more hard work than finding the complicated interface, and I don't think I would have figured out how to create something like jj until I'd seen it before.</p>
]]></description><pubDate>Wed, 15 Apr 2026 18:34:45 +0000</pubDate><link>https://news.ycombinator.com/item?id=47783260</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47783260</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47783260</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>I don't understand your example. Why haven't you added commit messages? Would you do that with git? In what situation are you creating different branches like that and not using either `jj commit` or `jj describe`?</p>
]]></description><pubDate>Wed, 15 Apr 2026 17:59:24 +0000</pubDate><link>https://news.ycombinator.com/item?id=47782800</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47782800</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47782800</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>Or you make tools that are easier to use, so that you can spend that week learning something more useful than the finicky details of branch vs detached head checkouts.</p>
]]></description><pubDate>Wed, 15 Apr 2026 01:37:14 +0000</pubDate><link>https://news.ycombinator.com/item?id=47773647</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47773647</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47773647</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>The only thing that changed in the two things you wrote was `ranch` -> `cdef`. Every other part of that PS1 output was the same.<p>Now put yourself in the shoes of a git novice and ask yourself if you'd always notice the difference. At least from my experience, they often don't, especially if they're concentrating on something else, it if they're using an IDE and the visual information about which branch/commit is checked out.<p>I don't think you're crazy, I think you're just too used to this sort of stuff to remember what it was like to still be learning git. When I say people make these sorts of mistakes, I'm thinking about real colleagues of mine who have made exactly these mistakes and then panicked that commits suddenly had disappeared.<p>Similarly, I think to you, unnamed branches feel like something complicated because in git that are. Git makes it very easy for commits to seemingly disappear into the ether, even though they are still there. But in jj, they don't disappear - they remain very visible, and the log UI shows them in a way that makes it clear where they come from. The default log UI is something like git's --graph output, which means you see how the different commits interact with each other. I really recommend having a look at the output of `jj log`, because I think then it'll be a lot clearer what I mean when I say that it's not hard to figure out what the right commit is.</p>
]]></description><pubDate>Tue, 14 Apr 2026 23:12:40 +0000</pubDate><link>https://news.ycombinator.com/item?id=47772655</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47772655</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47772655</guid></item><item><title><![CDATA[New comment by MrJohz in "GitHub Stacked PRs"]]></title><description><![CDATA[
<p>I think there's a sense that magit and jj are in some way equivalent tools, although I don't have enough experience with magit to be sure. They both sit in top of git and expose the underlying model of git far more cleanly and efficiently than the standard git cli. The difference is that magit uses interactivity to make git operations clearer, whereas jj tries to expose a cleaner model directly.<p>That said, there are additional features in jj that I believe aren't possible in magit (such as evolog/interdiffing, or checked-in conflicts), whereas magit-like UIs exist for jj.<p>You want the history of a specific commit because if you, say, fixup that commit, you want to know how the commit has changed exactly over time. This is especially useful for code review. Let's say you've got a PR containing a refactor commit and a fix commit. You get a review the says you should consider changing the refactor slightly, so you make that change and squash it into the existing refactor commit. You then push the result - how can the reviewer see only the changes you've made to only the refactor commit? That is an interdiff.<p>In this case, because you've not added any new commits, it's trivial to figure out which commit in the old branch maps to which commit in the new, fixed branch. But this isn't possible in general (consider adding new commits, or reordering something, or updating the commit message somewhere). In jj, each commit also has a change ID, and if multiple commits share the same change ID, then they must be different versions of the same changeset.<p>You want the history of the repository which includes the history of each commit, because it's a lot easier to type `jj undo` to revert an operation you just did than it is to find the old state of the repository in the reflog and revert to it, including updating all the branch references to point at their original locations. The op log in jj truly is the ultimate undo tool - it contains every state the repository has every been in, including changes to tags and branches that aren't recorded in the reflog, and is much easier to navigate. It is strictly more powerful than the reflog, while being simpler to understand.</p>
]]></description><pubDate>Tue, 14 Apr 2026 23:00:18 +0000</pubDate><link>https://news.ycombinator.com/item?id=47772551</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47772551</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47772551</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>They look identical to people who don't know what to look for, and who don't realise that these two states are different, which is the key thing. You can also distinguish them by running `git status`, but that's kind of the point: there's some magic state living in .git/ that changes how a bunch of commands you run work, and you need to understand how that state works in order to correctly use git. Why not just remove that state entirely, and make all checkouts behave identically to each other, the only difference being which files are present in the filesystem, and what the parent commit was?<p>What's wrong with unnamed branches? I mean, in git the main issue is that they're not surfaced very clearly (although they exist). But if you can design an interface where unnamed branches are the default, where they're always visible, and where you can clearly see what they're doing, what's wrong with avoiding naming your branches until you really need to?<p>I think this is the key thing that makes jj so exciting to me: it's consistently a simpler mental model. You don't need to understand the different states a checkout can be in, because there aren't any - a checkout is a checkout is a checkout. You don't need to have a separate concept of a branch, because branches are just chains of commits, and the default jj log commands is very good at showing chains of commits.</p>
]]></description><pubDate>Tue, 14 Apr 2026 19:23:45 +0000</pubDate><link>https://news.ycombinator.com/item?id=47770206</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47770206</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47770206</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>I think the right intuition to have with jj is that `jj st` should show an empty change unless you are actively working on something. `jj commit`, as mentioned below, is a good example of this - it automatically creates a new change and checks it out. The "squash flow" also does this well - you use the branch tip as a staging area and squash work into other changes on the branch as you go along. Either way, once the work is finished, there's an empty change at the tip of the branch.<p>This is also supported by jj implicitly - whenever you check out a different commit, if the change you were on is empty, has no description, and is the tip of a branch, it's automatically deleted to clean things up for you.</p>
]]></description><pubDate>Tue, 14 Apr 2026 19:08:53 +0000</pubDate><link>https://news.ycombinator.com/item?id=47769972</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47769972</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47769972</guid></item><item><title><![CDATA[New comment by MrJohz in "jj – the CLI for Jujutsu"]]></title><description><![CDATA[
<p>But branches are <i>not</i> just named pointers to a commit. If they were, then checking out the pointer would be the same as checking out the commit itself. But I can check out a commit and I can check out a branch and depending on which I've done, I'm in two different states.<p>Either I'm in branch state, where making a commit bumps the branch pointer and means the commit will be visible in the default log output, or I'm in "detached head" mode, and making a commit will just create a new commit somewhere that by default is hidden into I learn what a reflog is. And the kicker is: these two states look completely identical - I can have exactly the same files in my repository, and exactly the same parent commit checked out, but the hidden mode changes how git will respond to my commands.<p>In fairness, none of this is so difficult that you can't eventually figure it out and learn it. But it's not intuitive. This is the sort of weirdness that junior developers stumble over regularly where they accidentally do the wrong kind of checkout, make a bunch of changes, and then suddenly seem to have lost all their work.<p>This is one of the ways that I think the JJ model is so much clearer. You always checkout a commit. Any argument you pass to `jj new` will get resolved to a commit and that commit will be checked out. The disadvantage is that you need to manually bump the branch pointer, but the advantage is that you don't necessarily need branch pointers unless you want to share a particular branch with other people, or give it a certain name. Creating new commits on anonymous branches is perfectly normal and you'll never struggle to find commits by accidentally checking out the wrong thing.</p>
]]></description><pubDate>Tue, 14 Apr 2026 18:24:37 +0000</pubDate><link>https://news.ycombinator.com/item?id=47769327</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47769327</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47769327</guid></item><item><title><![CDATA[New comment by MrJohz in "GitHub Stacked PRs"]]></title><description><![CDATA[
<p>I find rebases are only a footgun because the standard git cli is so bad at representing them - things like --force being easier to write than --force-with-lease, there being no way to easily absorb quick fixes into existing commits, interdiffs not really being possible without guesswork, rebases halting the entire workflow if they don't succeed, etc.<p>I've switched over pretty much entirely to Jujutsu (or JJ), which is an alternative VCS that can use Git as its backend so it's still compatible with Github and other git repos. My colleagues can all use git, and I can use JJ without them noticing or needing to care. JJ has merges, and I still use them when I merge a set of changes into the main branch once I've finished working on it, but it also makes rebases really simple and eliminates most of the footguns. So while I'm working on my branch, I can iteratively make a change, and then squash it into the commit I'm working on. If I refactor something, I can split the refactor out so it's in a separate commit and therefore easiest to review and test. When I get review feedback, I can squash it directly into the relevant commit rather than create a new commit for it, which means git blame tends to be much more accurate and helpful - the commit I see in the git blame readout is always the commit that did the change I'm interested in, rather than maybe the commit that was fixing some minor review details, or the commit that had some typo in it that was fixed in a later commit after review but that relationship isn't clear any more.<p>And while I'm working on a branch, I still have access to the full history of each commit and how it's changed over time, so I can easily make a change and then undo it, or see how a particular commit has evolved and maybe restore a previous state. It's just that the end result that gets merged doesn't contain all those details once they're no longer relevant.</p>
]]></description><pubDate>Mon, 13 Apr 2026 22:44:09 +0000</pubDate><link>https://news.ycombinator.com/item?id=47758897</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47758897</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47758897</guid></item><item><title><![CDATA[New comment by MrJohz in "Lies I was told about collaborative editing, Part 2: Why we don't use Yjs"]]></title><description><![CDATA[
<p>In our case, we're not using a text editor, but instead building a spreadsheet, so a lot of these collab-built-into-an-editor are, like you say, pedagogically useful but less helpful as direct building blocks that we can just pull in and use.  But the advice is very useful, thank you!</p>
]]></description><pubDate>Mon, 16 Mar 2026 11:11:12 +0000</pubDate><link>https://news.ycombinator.com/item?id=47397493</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47397493</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47397493</guid></item><item><title><![CDATA[New comment by MrJohz in "Lies I was told about collaborative editing, Part 2: Why we don't use Yjs"]]></title><description><![CDATA[
<p>I've looked through the site, and right now it's probably the thing I'd try out first, but my main concerns are the missing documentation, particular the more cookbook-y kinds of documentation — how you might achieve such-and-such effect, etc.  For example, the sync example is very terse, although I can understand why you'd like to encourage people to use the more robust, paid-for solution!  Also just general advice on how to use DocNode effectively from your experience would be useful, things like schema design or notes about how each operation works and when to prefer one kind of operation or structure over another.<p>All that said, I feel like the documentation has improved since the last time I looked, and I suspect a lot of the finer details come with community and experience.</p>
]]></description><pubDate>Mon, 16 Mar 2026 11:06:23 +0000</pubDate><link>https://news.ycombinator.com/item?id=47397466</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47397466</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47397466</guid></item><item><title><![CDATA[New comment by MrJohz in "Lies I was told about collaborative editing, Part 2: Why we don't use Yjs"]]></title><description><![CDATA[
<p>Are there any major libraries for OT?  I've been looking into this recently for a project at work, and OT would be completely sufficient for our use case, and does look simpler overall, but from what I could tell, we'd need to write a lot of stuff ourselves.  The only vaguely active-looking project in JS at least seems to be DocNode (<a href="https://www.docukit.dev/docnode" rel="nofollow">https://www.docukit.dev/docnode</a>), and that looks very cool but also very early days.</p>
]]></description><pubDate>Mon, 16 Mar 2026 08:18:50 +0000</pubDate><link>https://news.ycombinator.com/item?id=47396337</link><dc:creator>MrJohz</dc:creator><comments>https://news.ycombinator.com/item?id=47396337</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47396337</guid></item></channel></rss>