11 ms·
jj: a Git-compatible VCS that is both simple and powerful
- iFire 2y agoHow does it compare against Facebook's Sapling? https://github.com/facebook/sapling https://github.com/facebook/sapling
- rjzzleep 2y ago> Mercurial & Sapling: There are many Mercurial-inspired features, such as the revset language to select commits. There is no explicit index or staging area. Branches are "anonymous" like Mercurial, so you don't need to make up a name for each small change. Primitives for rewriting history are powerful and simple. Formatting output is done with a robust template language that can be configured by the user. Can anyone explain why people want something like JJ? Is it simplicity? I actually quite like the staging and index in git. Although it took me a while to grok. Looking at the other thread though I don't think it's more simple. It's just different. https://news.ycombinator.com/item?id=43020180 https://news.ycombinator.com/item?id=43020180
- MrJohz 2y agoI agree that you probably want to keep staging, but one of the advantages of JJ is that staging is just a change* like any other change in your history. There are a couple of different ways that people use JJ, but I normally use the "squash" approach. When I want to make a new branch/pr/etc, I run `jj new master` (to create a new change off the master bench), and then I run `jj new` again. This gives me two empty changes - the older change is where I'm going to store my work when I'm finished, and the newer change is where I'm going to actually be working. This functions like staging. As I edit files, the staging change fills up. When I think I'm finished, I can run `jj diff` to exactly what's in my staging change. If I want to keep all of it, I can run `jj squash` to squash the entire change into the parent change, or I can run `jj squash -i` to interactively squash the bits that I want to keep, and keep the rest in staging. You might argue that we've just reinvented the wheel - we used to have a pseudo-commit as our staging/index, and now we have a JJ change, but we're still doing the same thing with it. This is true, but the value of having our staging area be a change, i.e. a first class entity in our VCS, is that we don't need to special case it any more. Every command that works for our staging area works for any other change in our history. This is simpler because there's less stuff going on (fewer commands, fewer concepts), but we still have the same capabilities as before. On top of that, because our staging area is just another change, it is automatically stored in the local repository history. This means that if I've got stuff staged and I want to create a new branch somewhere else, I can do `jj new master`, and the stuff I've got staged will stay where it is. This also removes the need for an explicit stash: if all the work I'm doing is already part of a change, I don't need to create temporary "stash" commits to store them, they're already stored. Again, fewer commands, fewer concepts. Just by itself, I find this feature really helpful because it simplifies how I need to think about changes a lot. It's so much easier to navigate around a repository like this. But it's just one of the features that has been simplified from existing VCSs. For example, you've also got automatic rebases - you can make a change to an older change (either directly or using something like `jj squash`), and all the parent change will automatically get rebased. Sometimes this causes conflicts, but it never fails. Instead, conflict tracking is part of the history itself. This allows you to fix conflicts in your own time - you're not put into the "rebase world" like in Git, where Git needs to statefully remember that it's currently doing a rebase, and you need to use different commands to resolve a rebase conflict than you need to commit a merge conflict, say. No, conflicts are first-class, which again massively simplifies how you interact with the repository - fewer commands, fewer concepts. This is what I think people mean by simple - it's not simple in the sense of "make everything easy by wrapping it in an abstraction", instead it's simple in the sense of "find the ideal underlying model to expose to the user". Version control is always going to be fairly complex, but JJ feels like something that is very close to the minimum complexity required for a VCS: no simpler (because then it wouldn't work very well), but also but very much more complex. * "Change" in this context refers to JJ's equivalent of commits, i.e. the individual rows you see when your run `jj log`.
- kemayo 2y agoI'm sympathetic to the simplicity argument. I've recently had to help someone who's new to coding with some getting-started-with-git issues, and it's definitely reminding me of the appeal of a system that you can't screw up in various interesting manners and then need someone who understands its inner workings to fix.
- MForster 2y agoYou can still use a similar workflow as with the index. The difference is that the staging area is modeled as a commit, so it's not another concept that will take a while to grok. Same with the working copy.
- mpalmer 2y agojj is nicer to learn first, but also, knowing both git and jj I do pick jj. Significantly nicer UI, very simple but powerfully expressive DSLs for formatting logs and selecting revisions to log, respectively. For me it clicked when I really grasped how the latter DSL (called the revset language) wasn't just for selectively logging, it was for selecting commits/changes to act upon. The revset grammar is about specifying subgraphs of the graph of changes. Using it, and taking advantage of jj's by-design automatic rebasing of downstream commits, you'll eventually think nothing of rebasing N branches in a single command. Even conflicts are less trouble when they crop up during auto-rebasing. jj leaves conflicts on the change where they originated, and marks that commit (and all downstream ones) as conflicted. Fixing conflicts is no longer a "drop everything and fix" matter; you can decide when to fix them without delaying work on other parts of the tree. Haven't even gotten into the optional auto committing on every file change (it's better than it sounds). You might miss the staging area for a bit, and then you realize that there isn't really a difference between the staging area and a "current commit" that stays up to date on its own. I guess what I'm saying is I would recommend it.
- recursive 2y agoI've wished I could turn the staging area off in git since before jj was a thing. If you like how git works, then you probably prefer it to the alternatives. There are things I like about git, but some of its design choices are hard for me to understand.
- sunshowers 2y agoJujutsu has all of the power of the index without actually having the index as a separate construct -- index operations become commit operations. It is quite meaningfully simpler than Git as it can express the same degree of power (actually a lot more) through fewer concepts.
- arxanas 2y agoI don't think the cheat sheet in that thread demonstrates any of jj's improved expressive power, such as revsets or first-class conflicts. I gave an example of something difficult do in Git here: https://news.ycombinator.com/item?id=43022367 https://news.ycombinator.com/item?id=43022367
- rtpg 2y agojj makes it super easy to "fix up" your commit tree to what you want it to look like. "Oh I forgot to make a change in this commit and I'm 4 commits in..." -> "I can go change that commit and everything else magically rebases on it". It treats changes as this conceptual thing, and the whole immutable commit thing as a backing store but not how _you_ are looking at a problem. Or at least not how I look at it. I added a test, put in a new feature, updated the readme. Those changes are not about all the metadata around the change, they're the change. Of course you still have the immutable commit graph when you want to _really specifically_ talk about a certain commit, but for most intents and purposes it's the changes that matter. jj gives you similar workflows to git in the easy case. In the hard cases, jj makes it easier to do the work than git. So it's nicer!
- gpm 2y ago> jj makes it super easy to "fix up" your commit tree to what you want it to look like. I hit this today in a fun way. I was working along as one does, and needed to write a parser for a simple s-expression based language. So I did that fairly quickly, added a few tests which appeared to pass, and kept on working. A few commits later I reached a point where I needed to manually test my code. And promptly hit a stack overflow in the parser. A few minutes of debugging I understood the issue (I'd used the wrong function and failed to require brackets around a nested s-expression) - except I could have sworn my tests should catch it. So I ran my tests manually, and lo and behold they did catch it. Turns out that there was a bug in the tool I was using to run my tests [1] that was masking the test failure. I had completely broken my tests for the last few commits (all of them since I wrote the parser). Anyways, I went and fixed the tool, but by now on my main repo I had fixes for the last few commits all mixed together with a fairly large WIP commit. Thankfully instead of making this the frustrating game of rebasing with git, jj makes it just take a few jj split (select changes you want to split off, sort like git add -p); jj squash (merging changes into the existing commit) commands. Could I have fixed up the previous commits with git? Of course. But it would have made a frustrating hour even more frustrating. [1] https://github.com/Canop/bacon/issues/326 https://github.com/Canop/bacon/issues/326
- Izkata 2y ago
- MForster 2y agohttps://jj-vcs.github.io/jj/latest/sapling-comparison/ https://jj-vcs.github.io/jj/latest/sapling-comparison/
- Shish2k 2y ago> Sapling supports cloning, pushing, and pulling from a remote Git repo. jj also does, and it also supports sharing a working copy with a Git repo, so you can use jj and git interchangeably in the same repo As a minor update - sapling now also supports the .git on-disk formats so that you can use git and sl interchangeably in the same repo
- mmmulani 2y agoI tried out both for a week each and much prefer Sapling. jj always, automatically turns your current state (even if it's empty) into a "change" which is like a commit (it has a hash). in practice, this was actually incredibly annoying. when working in git, you often think about the current commit you're on, and amending/making a new commit. with jj, you're actually making "changes" (which are like commits) constantly. it's also so infuriating that there are two sets of hashes, jj changes and git commits, and they are not the same/interchangable. on the flip side, sapling has been a breeze. it does force you to one commit per PR (which might be annoying to some but if you're going to squash the commit into main when you commit, why not do it locally too). its inter-op with github is really nice, you can do `sl goto pr1234` and it will just bring you to the code for pr #1234 (and fetch it in the process if you don't have it locally).
- jjfanboy 2y ago> . when working in git, you often think about the current commit you're on, and amending/making a new commit. with jj, you're actually making "changes" (which are like commits) constantly I don't know what this means, and it seems like a fairly large misunderstanding.
- stouset 2y agoWhat did you find annoying or infuriating about this? It’s quite possibly jj’s most beloved feature. Since all of your changes are constantly being recorded, you're covered by your VCS even if you don’t commit. As a 1+ year jj user now, I can’t think of a single time I’ve needed to use a git hash. They’re there underlying things, but you’re always just using revision IDs.
- eitland 2y agoHaven't tried jj, but one thing that immediately strikes me is that for us who use IDEs, git hashes are all that we can see in that view. I've been considering trying out jj and thought it could work since it uses git as store, but if I need to keep track of different hashes based on if I access version control from my IDE (primarily IntelliJ for now) or from my command line, that seems like an actual problem to me. (I'm also used to work in the command line and I prefer it to point and click for many things, but version control is one of the things that I much prefer do approach from the same place I write my code.)
- sophiebits 2y agoThe first testimonial on this page is from Rain who has over 1,000 commits to the Sapling project and provides some context: https://jj-vcs.github.io/jj/latest/testimonials/#what-the-users-have-to-say https://jj-vcs.github.io/jj/latest/testimonials/#what-the-us...
- sunshowers 2y ago(Hi Sophie!) I still stand by my testimonial! My greatest achievement has been convincing Steve Klabnik to try out Jujutsu. I also saw a comment from JJ's lead author martinvonz, where he pointed out that adding new functionality is much simpler to jj than it is to older systems like Git and Sapling/Mercurial. Having each spent many years working on source control, both Martin and I came to a general belief that a lot of implementation complexity comes from the modal states created by merge conflicts. Because Jujutsu's core UX is more straightforward, this is less of an issue and Jujutsu's devs can prototype changes quicker.
- Timothee 2y ago> My greatest achievement has been convincing Steve Klabnik to try out Jujutsu I came across jj from a recent Bluesky post by Steve Klabnik who was talking about having to learn something with git. That seemed very odd to me. I then gathered that he (and many others in the comments) had been using jj exclusively for some time. I haven't had time to give it a try, but I definitely will. Your achievement has ripple effects.
- nateb2022 2y agoRelated: Jujutsu (jj), a Git compatible VCS (89 points, 112 comments) https://news.ycombinator.com/item?id=41895056 https://news.ycombinator.com/item?id=41895056 Steve's Jujutsu Tutorial (158 points, 117 comments) https://news.ycombinator.com/item?id=41881204 https://news.ycombinator.com/item?id=41881204 Jujutsu: A Next Generation Replacement for Git (94 points, 80 comments) https://news.ycombinator.com/item?id=40908985 https://news.ycombinator.com/item?id=40908985 A better merge workflow with Jujutsu (135 points, 90 comments) https://news.ycombinator.com/item?id=40842762 https://news.ycombinator.com/item?id=40842762 Jujutsu: A Git-compatible DVCS that is both simple and powerful (673 points, 262 comments) https://news.ycombinator.com/item?id=36952796 https://news.ycombinator.com/item?id=36952796
- password4321 2y agoPossibly most recently (last week), with many anecdotal recommendations: 2025-02-04 https://news.ycombinator.com/item?id=42934427 https://news.ycombinator.com/item?id=42934427 (234 points, 122 comments) Jujutsu VCS: Introduction and patterns
- throwup238 2y agoThe most recent post is today, just two posts down on the front page as I write this: JJ Cheat Sheet (86 points, 39 comments) https://news.ycombinator.com/item?id=43020180 https://news.ycombinator.com/item?id=43020180 I assume that’s the inspiration for this link.
- lytigas 2y agoI've read a few small overviews of jj. One thing that's off-putting as a git lover is that while git is truly append-only (except refs), jj seems quite "mutable" by comparison. Say I'm messing around with the commit that introduced a bug, somewhere deep in the history. With git, it's basically impossible to mess up the repo state. Even if I commit, or commit --amend, my downstream refs still point to the old history. This kind of sucks for making stacked PRs (hello git rebase -i --autosquash --update-refs) but gives me a lot of confidence to mess around in a repo. With jj, it seems like all I would have to do is forget to "jj new" before some mass find+replace, and now my repo is unfixable. How does jj deal with this scenario?
- phildenhoff 2y agoIf you want to “checkout” some previous commit, jj has your back in three ways - first, that commit that’s been merged to main is marked as immutable and, unless you add a flag to say “I know this is immutable and I want to mutate it anyway”, you can’t mutate it - second, as part of your regular workflow, you haven’t actually checked out that historical commit. You created a new, empty commit when you “checked it out” using “jj new old_commit” - third, you can use jj undo. Or, you can use “jj obs log” to see how a change has evolved over time (read: undo your mass find+replace by reverting to a previous state)
- ninjamuffin99 2y ago“jj op log” shows you the operation history, which you can then “jj op restore” and point to where you want to restore to :) (disclaimer: im still jj newbie, but this has gotten me out of the snafus ive put myself into while learning g)
- OccamsMirror 2y agoCan I ask what your motivation was for trying jj? I'm always keen to explore new things but I don't have many complaints about git. I'm wondering what this solves that made it attractive for you.
- 2y ago
- SoftTalker 2y agoAlso, https://www.gameoftrees.org/ https://www.gameoftrees.org/
- benrutter 2y ago5 star logo right there!
- iFire 2y agoSee also https://github.com/gitless-vcs/gitless https://github.com/gitless-vcs/gitless. Looking for maintainers!
- frank-miao 2y agoI wonder if jj can integrate with vscode?
- gpm 2y agoWith colocated git repositories the part I really care about - marking what has changed and giving me diffs - just works out of the box. There's also https://www.visualjj.com/ https://www.visualjj.com/ if you want to make commits in your editor, though personally (both with git and jj) I've always preferred doing that from the terminal.
- tiffanyh 2y agoIt’s too bad that OpenBSD doesn‘t use JJ, instead developing GoT (even though they haven’t adopted using it yet, and still use CVS). Is that only because GoT predates JJ by 2-years? https://gameoftrees.org/index.html https://gameoftrees.org/index.html
- ninjin 2y ago"Only" seems to do a lot of work there. It is very difficult to use something that does not exist at a point when you have an itch to scratch. '^^ I can not speak for GoT, but I do see several obvious issues. One, jj is Apache 2.0 and it is explicitly considered undesirable by the OpenBSD developers as it imposes further restrictions than their license of choice [1]. Two, it is written in Rust and not only do I doubt that it can be ported to the platforms OpenBSD wants to support [2], but it may very well be impossible to build on said platforms which to the best of my knowledge is a strict requirement for inclusion. [1]: https://www.openbsd.org/policy.html https://www.openbsd.org/policy.html [2]: https://www.openbsd.org/plat.html https://www.openbsd.org/plat.html It should be noted that while GoT is developed by established OpenBSD developers, there is to the best of my knowledge no concrete plan to replace CVS at this point. While it may happen (and I personally hope it will as GoT is a very nice tool in my book), it is still up in the air and left to the rest of the OpenBSD developers when the time comes.
- ozornin 2y agoI have colleagues who are big fans of it. I gave it a try, but I really don't get what is the problem it is trying to solve. Like, what is it that I will be able to do that I couldn't do with git, and why bother learning it at all
- martinvonz 2y agoHave you tried asking your colleagues? What did they say?
- ozornin 2y agoI considered it, but it would inevitably involve talking to people, and it is a little too far for me
- baq 2y agoIf you ever did stacked prs and then had to rebase them all on top of each other, this is why. Otherwise I also find it not really an improvement and a downgrade in terms of other tooling (editor etc.)
- ks6g10 2y agoI do this regularly with magit, but mby I am missing something? I.e. the typical Gerrit workflow
- Skinney 2y agoI find JJ to be what you get when you take a step back, evaluate everything that works in Git and try to find a design that accomplishes all those things with the fewest amount of concepts possible. Like a Git 2.0, if you will. You can do most of the same things, but it's easier to understand it intuitively so you don't need to google or give up on the more advanced usages. And the things you do everyday you can do with fewer steps. Couple that with colocated repos, meaning I can use JJ while everyone keeps on using Git, there wasn't really anything holding me back.
- 2y ago
- pinoy420 2y agoAside: I am want to know people who use anything other than the very basic git checkout, fetch, push, merge, rebase, stash, status commit, reset. What are you doing? I think I have used cherrypick once? Generally I open two copies of the same repo, copy paste the code I need and commit as a new commit - the former invariably ends up as a whole mess which takes longer than copy paste efforts of the “feature” I wanted to retrieve. This is a genuine question not rage bait. I have been in the game for 15 years. Surely there are mega complex things using git internals / obscure commands to run analytics in CI systems or hosted git solutions. But I want to know what you are doing in your workflow that requires “more” of git.
- rat87 2y agoWell for one thing even doing basic commands its easy to mess up if you don't understand it. Even people who are better at git still often don't know that much about it. I was the git person at our last job and Im not that good. Besides push, merge, rebase, stash, status commit I've used submodules and git-svn(both for converting a repo to git and for having a local copy for my own use). Rebasing when you run into conflicts is annoying, I much prefer to have intellij to guide me through the rebase then using the commandline. Submodule are annoying but I assume part of that is inevitable when you have two repos linked like that, some of it is probably gits fault.
- ImHereToVote 2y agoI second the copy paste solution. I don't understand devs that cherrypick.
- benrutter 2y ago> Have you ever wanted to store your version controlled repositories inside a Dropbox folder? Or continuously backup repositories to S3? No? Well, now you can! This made me chuckle. In all seriousness, does jj afford anything new by connecting to filesystems more directly? The main issue with git I've heard of is for projects, like video games for instance, that bundle text based code with a huge amount of non-text assets (audio, images etc). I don't understand what causes gitissues here, is it storage? Or is it more attempting to map differences between non-text assets. Curious about whether jj's filesystem usage help here.
- steveklabnik 2y agoThat claim has nothing to do with integrating with file systems. Issues with Dropbox and git are fairly well known https://stackoverflow.com/questions/19305033/why-is-putting-git-repositories-inside-of-a-dropbox-folder-not-recommended https://stackoverflow.com/questions/19305033/why-is-putting-... Jj interacts with files in a way that ensures this can’t happen. Jj is the same as git with regards to large non-text assets. But that’s just because it’s still young, it will gain good support eventually. (And yeah it’s about storage and also about diffs. How would you fix a merge conflict in a PNG?)
- sureglymop 2y agoThis probably exists but what I really want is a way to keep changes that are not yet commuted with branches they belong to. I know that I can stash but it's not the same because the stash becomes a mess. I also know that I could commit and later before pushing merge commits together into one that resolves the branch. But it would be hard for me to remember that I did that after switching branches and coming back. What I'm really getting at is, I want a system that records all changes I make and then let's me very selectively choose which of those are pushed to others. Git is definitely the least bad solution now but it's not as seamless as it could be.
- necauqua 2y agoWith what you described it sounds like jj is _literally_ exactly what you need, lol Give it a try, but also know that git branches and _topological_ branches are different things
- relistan 2y agoI just want to say that we all finally are using mostly the same VCS and that is actually an undervalued, beautiful thing. An actual “standard” that is almost universal. It used to be such a mess with all the VCS systems and having to know all of them to commit on various dependencies from different projects. I know that jj can be backed by git. But already it doesn’t need to be. Between this and other projects, I don’t have to squint too hard to see a future that looks like the past. I hope I’m wrong.
- baq 2y agogit is good (good enough even) but not perfect. I’d love to see pijul receive funding for a small team for a couple years but we’ve got what we’ve got. jj’s killer feature is git compatibility, obviously. For now it’s table stakes so we can pretend only git exists.
- motorest 2y ago> I just want to say that we all finally are using mostly the same VCS and that is actually an undervalued, beautiful thing. An actual “standard” that is almost universal. This bears repeating. To further frame the importance of this, we've started seeing git being used as the deployment tool of some notable mainstream software projects such as nvm. This is only possible if a VCS becomes ubiquitous and a de-facto standard.
- MrJohz 2y agoI think that's actually one of the nice things about JJ in comparison to many others (such as hg or Pijul): it uses Git essentially like a protocol or standard by which it communicates with other systems (and as a file-system standard, like using SQLite or something similar). Essentially, it agrees that Git has become a de-facto standard as a file format and as a protocol, but that doesn't mean it can't have a very different approach to that file format and protocol. It's similar to how HTTP can support a RESTful approach, or GRPC, or GraphQL or whatever else, and all these different systems can build on the same underlying technology.
- 2y ago
- v3ss0n 2y agoThis got spammed a lot but does anyone actually use it in the wild? We have This and Sapling (mercurial) for alternative git. Back in 2014 up til 2018 we used mercurial , development workflow is a lot smoother and better in DX wise vs git but soon after bitbucket decided to cut mercurial we have no choice but to use Git since there is literally no opensource or cloud service mercurial `hub` in existence. What you need for your CVS to be popular is not having a new CVS that do a thing a little bit different and better vs Git but to have a good GitHub alternative. In Git world there we have Gitea - which is very easy to setup and experience is much better than github , and gitlab for bigger orgs. Plus a good IDE support. Sapling and JJ need something like that - built in or selfhostable.
- Skinney 2y agoI'm using it for everything now. The beauty of JJ is that everyone else on my team can keep on using git if they want, even if I'm using JJ on the same repo.
- fragmede 2y agoGiven than jj is compatible with git servers, does it matter? If you prefer jj, you can just use it on your laptop to just get shit done, meanwhile using whatever git server you're given.
- sbaildon 2y agoMitchell of Hashicorp and Ghostty fame uses jj https://x.com/mitchellh/status/1845869748841832667 https://x.com/mitchellh/status/1845869748841832667
- MrJohz 2y agoJJ should be compatible with any existing Git server. The project is currently maintained on Github, and I believe all the maintainers use JJ to work on the project itself. At work, I use JJ a lot with repositories hosted on Azure Devops, and that also works well (in some ways, better than Github). This is using JJ's "Git" backend, which internally stores data as Git refs, and communicates with other servers the same way that Git does. There is a work-in-progress native backend, but this is still being developed and you need to explicitly opt into it if you want to try it out.
- wodenokoto 2y agoSo jujutsu tracks all files and record all changes and then when I’m ready to wrap up my changes as a PR, I basically squash my history and give my work a name and description? Is that a correct mental model for working with JJ?
- renerick 2y agoWhat helped me understand jj was concept of Changes against commits. Commits are immutable checkpoints, snapshots of the work done. Changes are mutable sets of, well, changes. Of course, commits are used by the git backend anyway, I'm talking about conceptual level. The most notable mental shift is that you create a Commit (git add && git commit) after you are done, whereas you create a Change (jj new) before starting working on it, and you don't need to explicitly save it after, you bookmark and push it, or start working on a new change. Consequently, there is no distinction between working directory, index and the commit history. You only have one concept - Change, and you edit them however you wish, whether it's newly created change, or a very old one, or a merge, or whatever else From my experience, you should be able to fit any git worklow in this model: - Anything that requires creating commits is jj new, merge is also jj new. - If you wish to only save part of the changes (like git add -p) you call jj split. - git squash is jj squash - amend is also squash - rebase is automatic (jj edit or jj new --{after,before}) - git stash is obsolete (everything is saved anyway, just run jj new) - etc.
- geenat 2y agoAuto-commit of large files and no way to un-bloat your repo is a showstopper for me no matter how good the DX might be. Large file handling needs to be sane in any new VCS, IMHO, as this is a main failing of git (..without the extra legwork of git-lfs). Edit: https://github.com/jj-vcs/jj/issues/80 https://github.com/jj-vcs/jj/issues/80 Could maybe bring jj up to parity with git here
- necauqua 2y agoThere's an anti-footgun heuristic that asks "are you sure", basically, if the file about to be tracked is >=1mb, saved a lot of asses a lot of times. But also, if it is such a showstopper, you can disable auto-amend of _new_ files with snapshot.auto-track=none() config (or even something funky like src/*) - and recently jj status finally started showing untracked files too, it's very usable
- deleted 2y ago[deleted]
- necauqua 2y agoIt's hilarious that there are people who "don't get what problem jj is trying to solve". It seems like they just use git so little, barely for more that linearly committing, so they are not aware of it's shortcomings that jj often elegantly fixes.