Threads for chriskrycho

  1. 3

    If you don’t want to plonk down full price for the latest machine, the best value for money today is to buy a used 2015 MacBook Pro off of somewhere like macofoalltrades.com. That isn’t going to have the performance of a recent M1 or M2, but it is a solid build with a decent keyboard and good trackpad and recent enough where it should have updates for a while to come.

    1. 1

      I can’t stand the keyboards on those machines. I still have a late 2013 15” MBP. The M1 versions are the first ones that seem a lot better, some of the newer Intel ones are a bit faster (a lot faster if you care about GPU performance, but I don’t really). We recently bought one for my partner because they’re absolutely dirt cheap now, and a quad-core Haswell CPU with 16 GiB of RAM is ample for a lot of things.

      Unfortunately, these are no longer supported with the latest macOS (which doesn’t stop Apple sending notifications telling me to upgrade) and so will stop getting security updates soon. I’ll probably get a newer Arm MBP at some point before that happens and install something else on the old one.

      I’m honestly amazed at how well this machine has stood up. It’s now 9 years old and is still getting daily use. I hope the new ones age this well!

      1. 1

        You’re thinking of the 2016-era machines. The 2013 MBP and the 2015 MBP have the same keyboard, which is the design that precedes the 2016–2019-era keyboardtastrophe.

        1. 1

          Huh, I thought the 2015 had the stupid touchbar thing instead of an escape key, but it looks as if that didn’t come in until 2016. The 2015 and 2014 ones were a small incremental speed increase over 2013. I think 2016 was around the time I started thinking ‘I’ve had this machine a while, I wonder if I should upgrade’ and was very disappointed by the newer models (significantly worse keyboard, marginally better CPU, same RAM limit).

        2.  

          The 2015 is the last one before they switched the keyboards. That’s why I recommend it: longest period of support ahead of it and newest hardware, but still really solidly built with a good keyboard.

      1. 2

        The MBA M2 is developing a reputation for running hot. https://arstechnica.com/gadgets/2022/07/the-new-macbook-air-runs-so-hot-that-it-affects-performance-it-isnt-the-first-time/

        My MBP M2 seems to work great and I’ve heard nothing but good things about the M1 machines.

        1. 5

          To be clear, the thermal throttling folks have found shows up when you out it under sustained “pro”-type workloads, and even then it’s faster than the M1 and has phenomenal battery life. Unless you’re simultaneously taxing all the CPU and GPU cores simultaneously for sustained periods in your daily usage, you’re unlikely to ever even hit those.

          1. 5

            Can confirm. Have a M2 Air and have not had any issues with Rust development

            1. 3

              Yep. My current PC laptop (Asus Zephyrus G14) doesn’t just run hot when it starts using the GPU. It crashes with a hard reboot. I’ll take throttling over that every single time.

            2. 2

              Shrug. The MacBook Air M1 also runs hot and starts throttling. I had a MacBook Air M1 for a while (now Pro 14”) and could get the CPU to heat up to 95 degrees and start dropping from 3.2GHz to 2.3 GHz very quickly (e.g. by building PyTorch).

              But it doesn’t matter. The machine is still perfectly fast even when it throttles. The MacBook Air trades-off a bit of sustained performance for being ultra-thin and fanless. Unless you are doing long builds, you won’t notice, because it won’t throttle with short computation bursts (e.g. your language server doing its work). If you prefer sustained performance, get a Pro 13” or even better, a Pro 14” or 16”. Those models take the other side of the trade-off - they much thicker and are actively cooled, but throttle (less) with sustained workloads.

              The whole M2 throttling saga is just clickbait of a Youtube’er and some news sites. Where were they when the M1 came out?

            1. 5

              Working more on my novel, which is advancing more steadily as I get better at writing badly.

              1. 3

                “Getting better at doing x badly” is such a great description of how practice actually works. 👏🏼

                1. 1

                  I may have misunderstood, but I would hope that practice based activities would get better with time.

                  In my case, which is a common for creative writing, there is a philosophy of turning off the inner critic.

                  If I worry about the quality of my writing, I will not write. So I am training myself to work without concern for quality. This advances the writing better, and once the words are on the page, they can always be revised and improved. I also find that once I am writing, the quality can be quite decent.

                  1. 2

                    That’s actually exactly what I meant. The only way to get better at things is to be okay with doing them “badly” so that incremental improvement can happen. Every time I start playing a piano piece (or writing music likewise) where I start is bad. By way of practice the level of things we can approach changes, but we’re always bad at the next level of challenge when we start. I heard it said by some famous bicyclist: “It never stops being hard, you just get faster.”

              1. 1
                • I finally got around to working through Bodil Stokke’s really fantastic tutorial for building a minimal parser combinator library in Rust from scratch. Had a fantastic day of 🤯 and 😁 working through it.
                • running, as usual: target 3–4 miles tomorrow, hopefully some good anaerobic pushing, and then a 7–8 mile “long” run (ramping up after COVID and then a very sick winter and spring afterward) on Sunday
                • maybe making some iterative progress on a large scale symphonic work tomorrow
                • end of month finances catch up
                • continuing to make progress on my photography backlog
                • church on Sunday, as always!
                1. 21

                  People keep writing stuff like the recently-posted https://v5.chriskrycho.com/journal/some-thoughts-on-zig/, so I figured it’s time to start talking about this for real.

                  Edit: Added some more info to readme to answer common questions, commit 13f6c2f14ab1a4a6aac31f176b0d25b5b0227a2b

                  1. 7

                    That Graydon Hoare quote at the end is really wholesome. Good luck on garnet!

                    1. 4

                      Well you certainly got my attention with it. 😅

                      1. 1

                        really wonderful to see this space being explored! garnet looks like it’s going to be great, looking forward to using it someday.

                      1. 2

                        I wonder if the problem with memory safety is less a problem of the “language”, narrowly defined, but much more of the totality of the programming environment. Like, it may not be possible to have a “small” language that provides memory safety if you need to support Linux or Windows or &c. Right?

                        1. 4

                          This is definitely a part of it, but it’s not the whole. An unsafe environment ups the importance of those issues—it means that a failure can result in unbounded problems. In a safe environment like WebAssembly, the cost is more constrained: at least so far, it means you’ll just crash rather than ending up scribbling all over someone else’s memory and therefore causing vulnerabilities. Being in a context where memory-unsafety doesn’t directly result in CVEs helps, in other words—but “it’ll just crash” still isn’t great!

                          1. 1

                            I just think that the idea that we can address memory safety at the C level and be fine is really misguided? Maybe it’s time to reconsider many of the architectural spandrels that we maintain and even lionize. I don’t know. I wish the Zig and Rust folks the best, because even impossible goals can yield impressive results.

                            1. 5

                              I think I would say the goal is different (at least for Rust): it’s not “ah, then we’re fine” but rather we desperately need defense in depth. Not least because even OS- or runtime-level safety can’t save you from side-channel attacks which rely on speculative execution at the hardware level! Yeah, we can introduce mitigations on the OS side for those, but fundamentally they illustrate that what we actually need is for our tools to help us produce software safely at every level of the stack.

                              1. 1

                                It makes perfect sense; I was thinking more about the language size issue.

                        1. 5

                          So when did an “enum” go from a series of abstract values:

                          enum month { January, February, March, April, ... };
                          

                          to a union of structures? Or is my C bias showing here?

                          1. 9

                            Note that a Rust enum is not quite a C union either; it is a discriminated/tagged union. I think the idea behind calling these enums in rust is you can explain it as “similar the enums you’re used to, but maybe with data attached to the variant.” I suspect union would still have been a better starting point pedagogically, but all I’ve got is a gut instinct based on a couple interactions, and anyway that ship has sailed.

                            1. 5

                              Also, it allowed Rust to reserve union for something which (unsafe-ly!) has the semantics of C-style unions. To the grandparent post: Rust’s enums do exactly what your example shows in the base case: if you write what you wrote there it will have the same behavior it would in C.

                              1. 3

                                I think union would have been even more confusing given the C/C++ baggage there. I think rust reused that keyword for exactly what it should be used for.

                                1. 2

                                  Enumerating values?

                                2. 3

                                  The enums I am used to are just named integer values, like the example I gave in C, or in Pascal:

                                  TYPE
                                      color = (red , green , blue);
                                      month = (January, February, March, ...);
                                  

                                  The C keyword enum is short for “enumeration”. As for discrimiated/tagged unions, you can do that too in C:

                                  typedef union _XEvent
                                  {
                                    int type;
                                    XAnyEvent xany;
                                    /* ... */
                                  } XEvent;
                                  
                                  typedef struct
                                  {
                                    int type;
                                    unsigned long serial;
                                    /* ... */
                                  } XAnyEvent;
                                  

                                  Yes, it’s not automatic, but it can still be done. And I don’t mind it being done automatically, it’s just in my head, that’s still a union, not an enum.

                                  1. 2

                                    Yep, of course you can build it with C! The key here (as I said on a different reply) is that the simplest form of enum does have the same semantics as the C/C++ idiom. This is perfectly valid Rust:

                                    enum Month { January, February, March, April, ... };
                                    

                                    And you can even, in the case where none of the variants carries data, explicitly give them discriminant values, just as in C:

                                    enum Month {
                                        January = 1,
                                        February = 2, // doesn't have to be specified, as in C/C++
                                        // ...
                                    }
                                    

                                    When you do this, you are enumerating a closed set of choices. It’s not a far jump to the idea that you might also want to enumerate a closed set of choices which include data—which is exactly what the rest of Rust’s enum capability is. It is as if you wrote two things to give yourself the ability to do exhaustiveness checking with a manually tagged union in C:

                                    • an enum for the tag
                                    • a union which always uses an enum value to distinguish the union fields, and where those are carefully maintained as disjoint

                                    Rust chose enum here because it allows this broader case (where it carries data) to be a generalization of the narrow case (where it is basically just a discriminant for the case), rather than having to switch modes entirely when you want to start carrying around data.

                                    1. 1

                                      Yeah, this is more or less the perspective I’ve gotten from basically every C programmer I’ve talked to who has a strong opinion.

                                  2. 3

                                    When Rust needed a non-jargon term for a sum type.

                                    1. 1

                                      And so they took a jargon term (enum) for another concept from their main “replacement target” language (C/C++) and used that, just to keep things interesting for all those C/C++ developers that wish to switch to Rust?

                                      To me there are only two plausible explanations for overloading enum like this: whomever made this decision didn’t think this through or it’s a big middle finger to C/C++. It’s in all likelihood the former and admitting mistakes were made is IMO a better way to deal with it.

                                      1. 5

                                        Why are you interpreting this as some kind of attack from Rust? As if C owned the word and had some exclusive use-patent for it. There is a common vocabulary used across programming languages and math, and terms are adopted between them when it helps with familiarity.

                                        C adopted some mathematical terms too, e.g. it has “integers” even though they’re not infinite! But int x is more understandable than e.g. ring x or some jargon about ℤ subsets, congruence relations, or modulo arithmetic.

                                        Naming is hard. There is a trade-off between being specific and familiar. In the case of sum types, “rebranding” as enum makes them more familiar and easier to understand for the target audience. It’s close enough that people get it “oh, it’s like the C enum, but can have non-integer values”. There are cases where Rust chose the other way: trait could have been interface or abstract class. There’s also a case where use of a familiar term IMHO backfired: reference. They’re sort-of like C++ references, but the name doesn’t capture the crucial ownership aspect. IMHO Rust would have been easier to learn if & was called a loan.

                                        1. 1

                                          Why are you interpreting this as some kind of attack from Rust?

                                          I do not and I don’t see how my comment could be interpreted as such. If anything, I am expressing my bewilderment at the decision to overload the enum meaning like this while pitching Rust as an alternative to C/C++ and trying to attract developers familiar with these languages.

                                          It’s close enough that people get it “oh, it’s like the C enum, but can have non-integer values”.

                                          I can tell you that when I (having 20+ years developing in C++) first read about Rust’s enum, that was not my reaction. And judging by this thread, I am not the only one.

                                          Naming is hard. […]

                                          I agree with the overall sentiment, but I think in this case it was not hard to foresee. In fact, I wonder who made this decision and if it’s documented somewhere? I would really like to see if this was even a consideration? Anyone has any pointers?

                                          1. 5

                                            There was discussion on the mailling list (a bit hard to read, as there are two threads intertwined):

                                            The general algorithm for finding this sort of thing:

                                            • go to rust pre-history: https://github.com/graydon/rust-prehistory
                                            • find the relevant test
                                            • find the test in the modern rust repo (test files are not renamed)
                                            • blame to find the commit that introduced the change
                                            • along the way, fish for keywords to search the mailling list (tag&enum) in this case

                                            My summary of the discussion:

                                            • the thing was actually called tag , this felt weird
                                            • variant was considered, but was too long (at that time, there was a hard limit of 5 chars for keywords)
                                            • union was considered, but it had wrong semantics: a C programmed typing union in Rust would be confused
                                            • enum was considered, a C programmer typing enum in Rust wold find that it just works.
                                            • there’s precedent for using enum for data-carrying variants in Java

                                            Note that how, in my other comment, I’ve re-creating all the arguments (including the Java one!) working backwards from the language values. It’s a weak evidence that this is indeed a value-driven design, rather than an arbitrary decision.

                                            1. 5

                                              I do not and I don’t see how my comment could be interpreted as such.

                                              TBH, the “there are only two plausible explanation” bit rubbed me a wrong way too: it has an unfortunate combination of being arguably wrong (there is a possibility that someone thought hard about this and made an informed decision for values other than spite) and emotionally loaded (can’t say that “middle finger” reads neutral to me).

                                              1. 3

                                                Ok, fair enough, I guess the enum choice seemed so obviously wrong to me that it made me think of less charitable explanations. But from your comment I can see how/why someone could reasonably arrive at such a choice (but still wrong, IMO). Also thanks for digging up the history on this, much appreciated.

                                          2. 5

                                            Well, there’s at least a third possible explanation: the designer thought carefully, and it it was the best choice according to their values.

                                            To me, the choice to use enum seems pretty defensible.

                                            • There’s already a precedent in attaching data to variants: Java’s enum do that (they require the same data for all variants, so they are not sum-types, but they are decidedly not C-style bunch of integer constants).
                                            • Another possible choice of keyword is union, but that arguably has even more different semantics. In particular, without associated data Rust’s enum is pretty close to C’s enum, while a C union without associated data is just an incoherent thing. Even more directly, rust has union keyword to declare unions, which are a different thing from enums.
                                            • Another choice would be variant. That’s probably the right term here. The problem with it is that it’s unfamiliar and doesn’t have precedent in C-style languages. This also leaves the enum unused, which creates needless difference with C-style enumerations
                                            • Rust also, by design, has short keywords, and it’s difficult to truncate variant without it being confused with variable.
                                            • Besides union, variant, and enumeration, I don’t think there are any other pre-existing terms for this semantic space. And inventing a new term (eg, pub choice Month { Jan, … }) goes way against the goal of familiarity.
                                            • Another option would be to get rid of the struct/enum keywords at all and use ML-style sigil syntax for specifying enums, but that seems to stray even further from C-style curly-braced typed declarations.

                                            Given Rust’s values of:

                                            • being familiar to C/C++ developers
                                            • bringing good “functional” ideas to practice without high-brow academic jargon
                                            • preference for terse keywords

                                            I personally don’t see any other choice Rust could’ve made here (but maybe I’ve missed some non obviously horrible alternative?)

                                            1. 1

                                              Well, there’s at least a third possible explanation: the designer thought carefully, and it it was the best choice according to their values

                                              Wouldn’t that be bad? Shouldn’t they use the agreed upon language values (e.g., those you list at the end of your comment) rather than their personal values?

                                              To comment on your other points: variant is pretty familiar to C++ developers: std::variant is in C++17 and was long before that in Boost and it was clear to everyone in the community that Boost’s design and name are being used as a base for the standardization. I also find the argument that the variant keyword is too long unconvincing: it’s only one character longer than struct and is a relatively low-frequency keyword, unlike, say, let and mut.

                                              1. 2

                                                Shouldn’t they use the agreed upon language values (e.g., those you list at the end of your comment) rather than their personal values?

                                                I use “language values” and “language designer” values interchangeably: these are very similar, especially in the early days of Rust (it can be argued that today Rust strays away a bit from Graydon’s original value design).

                                              2. 1

                                                If Rust valued being familiar with C/C++ developers, I think they failed in this case. I’m also failing to see why keywords have a limit of five characters (but I’m not interested in tracking that decision down, but it seems rather arbitrary to me). One other question—what is a Rust union then?

                                                1. 2

                                                  It’s an unsafe construct which has exactly the semantics of a C (untagged) union and was reserved for that exact purpose for interop.

                                                  See also my other comments in this discussion (on sibling threads, esp. here) showing how the C definition of an enum copied over to Rust does exactly what it would in C, which is a pretty good way of being “unsurprising” in my view!

                                                2. 1

                                                  How about data, which is used for sum types in Haskell?

                                                3. 4

                                                  As far as I can see, Swift uses the term enum in the same way Rust does:

                                                  https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html

                                              3. 1

                                                This is what happens if you force C++ syntax on an ML-like language.

                                              1. 2

                                                Since, as can be easily deduced from this article, a “struct” is equivalent to an “enum” wïth one branch, what’s the point of having this distinction at all?

                                                1. 2

                                                  There are things you can do with structs you cannot do with enums; in particular, creating a zero-sized struct which has no runtime cost but is useful for type-checking (struct Empty;). I don’t believe the emit for a single-variant discriminate-only enum is the same there: it still emits the discrimination I believe.

                                                  There’s also user-facing value in being able to define the data structure itself standalone, in terms of both convenience and usability, and that goes extra when composing together types.

                                                1. 4

                                                  Well, the “tuple” fields in a Swift enums case can be named as well.

                                                  1. 2

                                                    Ah, good point – I had forgotten that, because I’ve not usually seen it… except in function definitions, since that’s how args are modeled, which had entirely fallen out of my head until you mentioned this.

                                                    1. 2

                                                      In addition to that, if you really wanted to use structs there, you could move them inside the enum if that’s the only place you want to use them. This reduces the clutter in your top level namespace and makes it obvious that a value of that struct type came from that enum when the value is bound to a variable through a pattern match for example.

                                                  1. 18

                                                    Interestingly, I’ve recently been thinking that Rust is redundant here. Most of the time, when I take a shortcut and use, eg, a tuple struct, I come to regret it later when I need to add an .4th field.

                                                    I now generally have the following rules of thumb:

                                                    • Don’t use tuple structs or tuple enum variants. Rational: if you need to tuple at least two fields, you’d want to tuple three and four in the future, at which point refactoring to record syntax would be a chore. Starting with record syntax isn’t really all that verbose, especially now that we have field shorthand
                                                    • For newtype’d structs, use record syntax with raw / repr as a field name: struct Miles { raw: u32 }. Rationale: x.0 looks ugly at the call-site, x.raw reads nicer. It also gives canonical name for the raw value.
                                                    • “newtype” enum variatns are OK. Rational: you don’t .0 enum variant, there’s only pattern matching (trivia: originally, the only way to work with tuple-struct was to pattern-match it, the .0 “cute” hack was added later (which, several years down the line, required breaking lexer/parser boundary to make foo.0.1 work))
                                                    • In fact, probably most enums which are not just a private impl detail of some module should contain only unit/newtype variants: Rational: no strong rational here, this is the highest FPR rule of all, but, for larger enums, often you’ll fiend that you need to work with a specific variant, and splitting off enum variant into a dedicatded type often messes-up naming/module structure.
                                                    1. 8

                                                      My rule of thumb has been that tuples (struct or otherwise) should only very rarely cross module boundaries. However, if I’m just wiring a couple of private methods together, it’s often not worth the verbosity to definite a proper struct.

                                                      1. 3

                                                        That’s also reasonable! My comment was prompted by today’s incident, where I had to add a bool field to a couple of tuple-variants to a private enum used in tests :)

                                                      2. 2

                                                        My experience directly, almost word-for-word. I think the importance of having a convention for the “default” name instead of .0 makes all the difference and removes the biggest source of inertia when using a record struct instead of a tuple struct (you picked .raw, I picked .value - tomato tomato).

                                                        In fact, if I had to pick globally, I would take anonymous strongly-typed types (see C#) over tuples in a heartbeat. We just need a shorthand for how to express them as return types (some sort of impls?)

                                                        As an aside, again to steal prior art from C# (which, for its time and place is incredibly well-designed in many aspects) , record types and tuple types don’t have to be so different. Recent C# versions support giving names to tuple members that act as syntactic sugar for .0, .1, etc. If present at the point of declaration they supersede the numbered syntax (intellisense shows names rather than numbers) but you can still use .0, .1, etc if you want; they also remain “tuples” from a PLT aspect, eg the names don’t change the underlying type and you can assign (string s, int x) to a plain/undecorated (string, int) or a differently named (string MyString, int MyInt) (or vice-versa).

                                                        1. 2

                                                          This illustrates for me the kinds of decisions Swift makes in the language that you would otherwise have to settle on by policy and diligence. If you can’t combine two features in a certain way because there’s a better way to solve all the relevant real world problems, they would consider that a benefit to the programmer. That’s especially true when safety is involved, like Rust, but also just ease of use, readability, etc. I think it’s partly a reaction to C++ in which you can do anything and footguns are plentiful.

                                                          1. 2

                                                            Honestly, that’s my least favorite part of Rust. Since there’s always a bunch of ways to do the same thing, you have to be very careful to simultaneously avoid bike shedding and also avoid a wildly incident codebase.

                                                          2. 1

                                                            Could we put this in the docs somewhere?

                                                            1. 2

                                                              I don’t think this kind of advice is good for docs: it’s very opinionated, and Rust’s philosophy is very pluralistic. It’s also a bit dangerous, as it needs nuance to not be misapplied.

                                                              I do find that I’ve accumulated a lot of similar rules of thumbs and heuristics which help clarify thinking and make decisions when coding in the small, perhaps I’ll compile them in some long read one day.

                                                            2. 1

                                                              Huh, if only a tool existed that made refactoring these things easy and quick! (Jk, I bet rust-analyzer can do it!)

                                                              1. 1

                                                                The distinction between “newtype” and “tuple” is an interesting one I hadn’t considered, and I agree with it. Just(T) seems fine; my example with (T, U, V) not so much. I don’t share the dislike for newtype structs, but I also get where you’re coming from; Foo { field } is fine for both destructuring in a pattern match and for constructing an instance, so I agree that it doesn’t add that much these days.

                                                                I’m not entirely sure I follow your last bullet point; the stated preference and the argument seem to contradict each other, but probably I’m just misreading you?

                                                                1. 5

                                                                  Yeah, that’s confusing, more directly:

                                                                  // DO
                                                                  pub enum Expr { 
                                                                    AddExpr(AddExpr), 
                                                                    ... 
                                                                  }
                                                                  pub struct AddExpr { lhs: Expr, rhs: Expr }
                                                                  
                                                                  // DON'T
                                                                  pub enum Expr { 
                                                                    AddExpr { lhs: Expr, rhs: Expr }
                                                                    ... 
                                                                  }
                                                                  

                                                                  If you do the latter, you might find yourself wanting to refactor it later. But, again, this is very weak guideline, as adding an extra struct is very verbose. Eg, rust-analyzer doesn’t do this for expressions:

                                                                  https://github.com/rust-lang/rust-analyzer/blob/2836dd15f753a490ea5b89e02c6cfcecd2f32984/crates/hir-def/src/expr.rs#L175-L179

                                                                  But it does this for a whole bunch of other structs, to the point where we have a dedicated macro there:

                                                                  https://github.com/rust-lang/rust-analyzer/blob/2836dd15f753a490ea5b89e02c6cfcecd2f32984/crates/stdx/src/macros.rs#L22-L47

                                                                  1. 1

                                                                    Ah, so basically your rule here would be “just do what Swift requires you to do here.” I understand but… well, see the original post; I obviously disagree. 😂 I get why, from the refactoring POV and the “it’s useful to name use the inner type sometimes” but find that to be much more of a judgment call on how the type is used. I think I would find that design constraint more compelling as an option if there were a way to limit where you can construct a type that gets inherently exposed that way, while still allowing it to be matched against. You’d need something like Swift’s ability to overload its ~= to make that actionable, though, I think. 🤔

                                                              1. 16

                                                                After 5 years of using Mercurial I’m now at a new job using git and I want to murder myself. It’s so awful. And I used to work on git tooling two jobs ago so I’m not new to it.

                                                                I’m constantly performing unsafe operations. Rewriting history is somehow both unsafe and extremely painful. Maintaining small, stacked PR branches is nearly impossible without tooling like git-branchless.

                                                                I’ve convinced that anyone that says “git is not the absolute worst thing ever” has not invested enough time into learning better systems, even closely related ones like Mercurial.

                                                                Everyone using git is so distracted by their accomplishment of learning how to survive git’s UI and by reading blog posts explaining clean history and squashing and all this irrelevant philosophy that they forgot to examine if any of it was necessary.

                                                                1. 11

                                                                  Do you know of any good write-ups that explain to git users, in a constructive way, why none of it is necessary? I used SVN up until ~2010 when I switched to git, and my experience using git is far better than it ever was with SVN. I’ve never used mercurial. Any articles I can find that attempt to tell folks about better alternatives usually devolve (like your comment) into some git-bashing piece. Usually if you want to convince someone that they are doing the wrong thing, it’s not helpful to spend a lot of time telling them they are doing the wrong thing.

                                                                  Everyone using git is so distracted by their accomplishment of learning how to survive git’s UI and by reading blog posts explaining clean history and squashing and all this irrelevant philosophy that they forgot to examine if any of it was necessary.

                                                                  I don’t think it’s fair to say “everyone”, it sounds like you’re now using git after all :P

                                                                  1. 6

                                                                    I’ve only seen rants that give specific examples of how insane the command UI is without giving practical example of how’d you’d end up using those commands and rants that give concrete examples without showing alternatives. I agree with them but they don’t illustrate the problems to git users very well.

                                                                    I’m sure a good rant is out there but I can’t find it. Perhaps I need to write it instead of red-in-the-face ranting to lobsters and my friends :p

                                                                    1. 5

                                                                      Write it, I’d read it! :D

                                                                      1. 2

                                                                        Perhaps I need to write it instead of red-in-the-face ranting to lobsters and my friends :p

                                                                        I’ll be checking your user page so I don’t miss it :D

                                                                    2. 5

                                                                      Maybe jj/Jujutsu (mentioned in the article) is what you need instead of the actual git client. I personally find interactive rebase far more intuitive than branchless/jj commands…

                                                                      1. 5

                                                                        It’s really not just a question of intuitiveness, though. For example, how do you split an ancestor commit into two? An interactive rebase where you edit the commit, reset head, commit in parts, and then continue? What do you do with the original commit message? Store it temporarily in a text file before you reset? That’s madness. And the git add -p interface is embarrassing compared to hg split.

                                                                        I don’t mind interactive rebase but why are there no abstractions on top of it, and why is it so hard to use non-destructively?

                                                                        And thanks for the pointer, I’ll bump checking out jujutsu higher on my todo list.

                                                                      2. 3

                                                                        I’m constantly performing unsafe operations. Rewriting history is somehow both unsafe and extremely painful.

                                                                        I’ve literally destroyed hours or days of work by rewriting history in git so that it would be “clean”.

                                                                        Everyone using git is so distracted by their accomplishment of learning how to survive git’s UI and by reading blog posts explaining clean history and squashing and all this irrelevant philosophy that they forgot to examine if any of it was necessary.

                                                                        It is as though “git log” et al encourage a certain kind of pointless navel-gazing.

                                                                        1. 5

                                                                          I’ve literally destroyed hours or days of work by rewriting history in git so that it would be “clean”.

                                                                          Before doing complex operations, I run git tag x and can reset with git reset --hard x any time. (Using the reflog after the fact is also possible, but having a temporary tag is nicer to use.)

                                                                          1. 3

                                                                            I do the same but with git branch -c backup, and then git branch -d backup when I’m successfully done rebasing. I also often git push backup so I have redundancy outside my current working copy.

                                                                            And to the grandparent post: I find git log invaluable in understanding the history of code, and leave good commit messages as a kindness to future maintainers (including myself) because meaningless commit messages have cost me so much extra time in trying to understand why a given piece of code changed the way it did. The point of all of that is not to enable navel-gazing but to communicate the intent of a change clearly for someone who lacks the context you have in your head when making the change.

                                                                            1. 2

                                                                              This command will show you all the things your branch has ever been, so if a rebase goes wrong you can easily see what you might need to reset to. (Replace <branch> with your branch name.)

                                                                              BRANCH=<branch>; \
                                                                              PAGER="less -S" \
                                                                              git log --graph \
                                                                                      --decorate \
                                                                                      --pretty=format:"%C(auto)%h %<(7,trunc)%C(auto)%ae%Creset%C(auto)%d %s [%ar]%Creset" \
                                                                                      $(git reflog $BRANCH | cut '-d ' -f1)
                                                                              
                                                                            2. 3

                                                                              I’ve done incorrect history edits too, but I don’t think I’ve ever done one that I couldn’t immediately undo using the reflog.

                                                                          1. 40

                                                                            The interface of Git and its underlying data models are two very different things, that are best treated separately.

                                                                            The interface is pretty bad. If I wasn’t so used to it I would be fairly desperate for an alternative. I don’t care much for the staging area, I don’t like to have to clean up my working directory every time I need to switch branches, and I don’t like how easy it is to lose commit from a detached HEAD (though there’s always git reflog I guess).

                                                                            The underlying data model however is pretty good. We can probably ditch the staging area, but apart from that, viewing the history of a repository as a directed graph of snapshots is nice. Captures everything we need. Sure patches have to be derived from those snapshots, but we care less about the patches than we care about the various versions we saved. If there’s one thing we need to get right, it’s those snapshots. You get reproducible builds & test from them, not from patches. So I think Patches are secondary. I used to love DARCS, but I think patch theory was probably the wrong choice.

                                                                            Now one thing Git really really doesn’t like is large binary files. Especially if we keep changing them. But then that’s just a compression problem. Let the data model pretend there’s a blob for each version of that huge file, even though in fact the software is automatically compressing & decompressing things under the hood.

                                                                            1. 62

                                                                              What’s wrong with the staging area? I use it all the time to break big changes into multiple commits and smaller changes. I’d hate to see it removed just because a few people don’t find it useful.

                                                                              1. 27

                                                                                Absolutely, I would feel like I’m missing a limb without the staging area. I understand that it’s conceptually difficult at first, but imo it’s extremely worth the cost.

                                                                                1. 7

                                                                                  Do you actually use it, or do you just do git commit -p, which only happens to use the staging area as an implementation detail?

                                                                                  And how do you test the code you’re committing? How do you make sure that the staged hunks aren’t missing another hunk that, for example, changes the signature the function you’re calling? It’s a serious slowdown in workflow to need to wait for CI rounds, stash and rebase to get a clean commit, and push again.

                                                                                  1. 25

                                                                                    Do you actually use it

                                                                                    Yes.

                                                                                    And how do you test the code you’re committing?

                                                                                    rebase with --exec

                                                                                    1. 12

                                                                                      I git add -p to the staging area and then diff it before generating the commit. I guess that could be done without a staging area using a different workflow but I don’t see the benefit (even if I have to check git status for the command every time I need to unstage something (-: )

                                                                                      As for testing, since I’m usually using Github I use the PR as the base unit that needs to pass a test (via squash merges, the horror I know). My commits within a branch often don’t pass tests; I use commits to break things up into sections of functionality for my own benefit going back later.

                                                                                      1. 7

                                                                                        Just to add on, the real place where the staging area shines is with git reset -p. You can reset part of a commit, amend the commit, and then create a new commit with your (original) changes or continue editing. The staging area becomes more useful the more you do commit surgery.

                                                                                        1. 2

                                                                                          Meh, you don’t need a staging area for that (or anything). hg uncommit -i (for --interactive) does quite the same thing, and because it has no artificial staging/commit split it gets to use the clear verb.

                                                                                        2. 2

                                                                                          I guess that could be done without a staging area using a different workflow but I don’t see the benefit

                                                                                          I don’t see the cost.

                                                                                          My commits within a branch often don’t pass tests;

                                                                                          If you ever need to git bisect, you may come to regret that. I almost never use git bisect, but for the few times I did need it it was a life saver, and passing tests greatly facilitate it.

                                                                                          1. 9

                                                                                            I bisect every so often, but on the squashed PR commits on main, not individual commits within a PR branch. I’ve never needed to do that to diagnose a bug. If you have big PRs, don’t squash, or don’t use a PR-based workflow, that’s different of course. I agree with the general sentiment that all commits on main should pass tests for the purposes of bisection.

                                                                                        3. 3

                                                                                          I use git gui for committing, (the built in git gui command) which let’s you pick by line not just hunks. Normally the things I’m excluding are stuff like enabling debug flags, or just extra logging, so it’s not really difficult to make sure it’s correct. Not saying I never push bad code, but I can’t recall an instance where I pushed bad code because of that so use the index to choose parts of my unfinished work to save in a stash (git stash –keep-index), and sometimes if I’m doing something risky and iterative I’ll periodically add things to the staging area as I go so I can have some way to get back to the last known good point without actually making a bunch of commits ( I could rebase after, yeah but meh).

                                                                                          It being just an implementation detail in most of that is a fair point though.

                                                                                          1. 2

                                                                                            I personally run the regression test (which I wrote) to test changes.

                                                                                            Then I have to wait for the code review (which in my experience has never stopped a bug going through; when I have found bugs, in code reviews, it was always “out of scope for the work, so don’t fix it”) before checking it in. I’m dreading the day when CI is actually implemented as it would slow down an already glacial process [1].

                                                                                            Also, I should mention I don’t work on web stuff at all (thank God I got out of that industry).

                                                                                            [1] Our customer is the Oligarchic Cell Phone Company, which has a sprint of years, not days or weeks, with veto power over when we deploy changes.

                                                                                          2. 5

                                                                                            Author of the Jujutsu VCS mentioned in the article here. I tried to document at https://github.com/martinvonz/jj/blob/main/docs/git-comparison.md#the-index why I think users don’t actually need the index as much as they think.

                                                                                            I missed the staging area for at most a few weeks after I switched from Git to Mercurial many years ago. Now I miss Mercurial’s tools for splitting commits etc. much more whenever I use Git.

                                                                                            1. 1

                                                                                              Thanks for the write up. From what I read it seems like with Jujutsu if I have some WIP of which I want to commit half and continue experimenting with the other half I would need to commit it all across two commits. After that my continuing WIP would be split across two places: the second commit and the working file changes. Is that right? If so, is there any way to tag that WIP commit as do-not-push?

                                                                                              1. 3

                                                                                                Not quite. Every time you run a command, the working copy is snapshotted and becomes a real commit, amending the precis working-copy commit. The changes in the working copy are thus treated just like any other commit. The corresponding think to git commit -p is jj split, which creates two stacked commits from the previous working-copy commit, and the second commit (the child) is what you continue to edit in the working copy.

                                                                                                Your follow-up question still applies (to both commits instead of the single commit you seemed to imagine). There’s not yet any way of marking the working copy as do-not-push. Maybe we’ll copy Mercurial’s “phase” concept, but we haven’t decided yet.

                                                                                          3. 8

                                                                                            Way I see it, the staging area is a piece of state needed specifically for a command line interface. I use it too, for the exact reason you do. But I could do the same by committing it directly. Compare the possible workflows. Currently we do:

                                                                                            # most of the time
                                                                                            git add .
                                                                                            git commit
                                                                                            
                                                                                            # piecemeal
                                                                                            git add -p .
                                                                                            # review changes
                                                                                            git commit
                                                                                            

                                                                                            Without a staging area, we could instead do that:

                                                                                            # most of the time
                                                                                            git commit
                                                                                            
                                                                                            # piecemeal
                                                                                            git commit -p
                                                                                            # review changes
                                                                                            git reset HEAD~ # if the changes are no good
                                                                                            

                                                                                            And I’m not even talking about a possible GUI for the incremental making of several commits.

                                                                                            1. 7

                                                                                              Personally I use git add -p all of the time. I’ve simply been burned by the other way too many times. What I want is not to save commands but to have simple commands that work for me in every situation. I enjoy the patch selection phase. More often than not it is what triggers my memory of a TODO item I forgot to jot down, etc. The patch selection is the same as reviewing the diff I’m about to push but it lets me do it incrementally so that when I’m (inevitably) interrupted I don’t have to remember my place.

                                                                                              From your example workflows it seems like you’re interested in avoiding multiple commands. Perhaps you could use git commit -a most of the time? Or maybe add a commit-all alias?

                                                                                              1. 1

                                                                                                Never got around to write that alias, and if I’m being honest I quite often git diff --cached to see what I’ve added before I actually commit it.

                                                                                                I do need something that feels like a staging area. I was mostly wondering whether that staging area really needed to be implemented differently than an ordinary commit. Originally I believed commits were enough, until someone pointed out pre-commit hooks. Still, I wonder why the staging area isn’t at least a pointer to a tree object. It would have been more orthogonal, and likely require less effort to implement. I’m curious what Linus was thinking.

                                                                                                1. 2

                                                                                                  Very honourable to revise your opinion in the face of new evidence, but I’m curious to know what would happen if you broadened the scope of your challenge with “and what workflow truly requires pre-commit hooks?”!

                                                                                                  1. 1

                                                                                                    Hmm, that’s a tough one. Strictly speaking, none. But I can see the benefits.

                                                                                                    Take Monocypher for instance: now it’s pretty stable, and though it is very easy for me to type make test every time I modify 3 characters, in practice I may want to make sure I don’t forget to do it before I commit anything. But even then there are 2 alternatives:

                                                                                                    • Running tests on the server (but it’s better suited to a PR model, and I’m almost the only committer).
                                                                                                    • Having a pre push hook. That way my local commits don’t need the hook, and I could go back to using the most recent one as a staging area.
                                                                                                2. 1

                                                                                                  I use git add -p all the time, but only because Magit makes it so easy. If I had an equally easy interface to something like hg split or jj split, I don’t think I’d care about the lack of an index/staging area.

                                                                                                3. 6

                                                                                                  # most of the time

                                                                                                  git add .

                                                                                                  Do you actually add your entire working directory most of the time? Unless I’ve just initialized a repository I essentially never do that.

                                                                                                  Here’s something I do do all the time, because my mind doesn’t work in a red-green-refactor way:

                                                                                                  Get a bug report

                                                                                                  Fix bug in foo_controller

                                                                                                  Once the bug is fixed, I finally understand it well enough to write an automated regression test around it, so go do that in foo_controller_spec

                                                                                                  Run test suite to ensure I didn’t break anything and that my new test is green

                                                                                                  Add foo_controller and foo_controller_spec to staging area

                                                                                                  Revert working copy (but not staged copy!) of foo_controller (but not it’s spec)

                                                                                                  Run test suite again and ensure I have exactly one red test (the new regression test). If yes, commit the stage.

                                                                                                  If no, debug spec against old controller until I understand why it’s not red, get it red, pull staged controller back to working area, make sure it’s green.

                                                                                                  Yeah, I could probably simulate this by committing halfway through and then doing some bullshit with cherry-picks from older commits and in some cases reverting the top commit but, like, why? What would I gain from limiting myself to just this awkward commit dance as the only way of working? That’s just leaving me to cobble together a workflow that’s had a powerful abstraction taken away from it, just to satisfy some dogmatic “the commit is the only abstraction I’m willing to allow” instinct.

                                                                                                  1. 4

                                                                                                    Do you actually add your entire working directory most of the time?

                                                                                                    Yes. And when I get a bug report, I tend to first reproduce the bug, then write a failing test, then fix the code.

                                                                                                    Revert working copy (but not staged copy!) of foo_controller (but not it’s spec)

                                                                                                    Sounds useful. How do you do that?

                                                                                                    1. 7

                                                                                                      Revert working copy (but not staged copy!) of foo_controller (but not it’s spec)

                                                                                                      Sounds useful. How do you do that?

                                                                                                      You can checkout a file into your working copy from any commit.

                                                                                                      1. 6

                                                                                                        Yes. And when I get a bug report, I tend to first reproduce the bug, then write a failing test, then fix the code.

                                                                                                        Right, but that was just one example. Everything in your working copy should always be committed at all times? I’m almost never in that state. Either I’ve got other edits in progress that I intend to form into later commits, or I’ve got edits on disk that I never intend to commit but in files that should not be git ignored (because I still intend to merge upstream changes into them).

                                                                                                        I always want to be intentionally forming every part of a commit, basically.

                                                                                                        Sounds useful. How do you do that?

                                                                                                        git add foo_controller <other files>; git restore -s HEAD foo_controller

                                                                                                        and then

                                                                                                        git restore foo_controller will copy the staged version back into the working set.

                                                                                                    2. 1

                                                                                                      TBH, I have no idea what “git add -p” does off hand (I use Magit), and I’ve never used staging like that.

                                                                                                      I had a great example use of staging come up just yesterday. I’m working in a feature branch, and we’ve given QA a build to test what we have so far. They found a bug with views, and it was an easy fix (we didn’t copy attributes over when copying a view).

                                                                                                      So I switched over to views.cpp and made the change. I built, tested that specific view change, and in Magit I staged that specific change in views.cpp. Then I commited, pushed it, and kicked off a pipeline build to give to QA.

                                                                                                      I also use staging all the time if I refactor while working on new code or fixing bugs. Say I’m working on “foo()”, but while doing so I refactor “bar()” and “baz()”. With staging, I can isolate the changes to “bar()” and “baz()” in their own commits, which is handy for debugging later, giving the changes to other people without pulling in all of my changes, etc.

                                                                                                      Overall, it’s trivial to ignore staging if you don’t want it, but it would be a lot of work to simulate it if it weren’t a feature.

                                                                                                    3. 6

                                                                                                      What’s wrong with the staging area? I use it all the time to break big changes into multiple commits and smaller changes.

                                                                                                      I’m sure you do – that’s how it was meant to be used. But you might as well use commits as the staging area – it’s easy to commit and squash. This has the benefit that you can work with your whole commit stack at the same time. I don’t know what problem the staging area solves that isn’t better solved with commits. And yet, the mere existence of this unnecessary feature – this implicitly modified invisible state that comes and crashes your next commit – adds cognitive load: Commands like git mv, git rm and git checkout pollutes the state, then git diff hides it, and finally, git commit --amend accidentally invites it into the topmost commit.

                                                                                                      The combo of being not useful and a constant stumbling block makes it bad.

                                                                                                      1. 3

                                                                                                        I don’t know what problem the staging area solves that isn’t better solved with commits.

                                                                                                        If I’ve committed too much work in a single commit how would I use commits to split that commit into two commits?

                                                                                                        1. 4

                                                                                                          Using e.g. hg split or jj split. The former has a text-based interface similar to git commit -p as well as a curses-based TUI. The latter lets you use e.g. Meld or vimdiff to edit the diff in a temporary directory and then rewrites the commit and all descendants when you’re done.

                                                                                                          1. 3

                                                                                                            That temporary directory sounds a lot like the index – a temporary place where changes to the working copy can be batched. Am I right to infer here that the benefit you find in having a second working copy in a temp directory because it works better with some other tools that expect to work files?

                                                                                                            1. 1

                                                                                                              The temporary directory is much more temporary than the index - it only exists while you split the commit. For example, if you’re splitting a commit that modifies 5 files, then the temporary directory will have only 2*5 files (for before and after). Does that clarify?

                                                                                                              The same solution for selecting part of the changes in a commit is used by jj amend -i (move into parent of specified commit, from working-copy commit by default), jj move -i --from <rev> --to <rev> (move changes between arbitrary commits) etc.

                                                                                                          2. 2

                                                                                                            I use git revise. Interactive revise is just like interactive rebase, except that it has is a cut subcommand. This can be used to split a commit by selecting and editing hunks like git commit -p.

                                                                                                            Before git-revise, I used to manually undo part of the commit, commit that, then revert it, and then sqash the undo-commit into the commit to be split. The revert-commit then contains the split-off changes.

                                                                                                          3. 3

                                                                                                            I don’t know, I find it useful. Maybe if git built in mercurials “place changes into commit that isn’t the most recent” amend thing then I might have an easier time doing things but just staging up relevant changes in a patch-based flow is pretty straightforward and helpful IMO

                                                                                                            I wonder if this would be as controversial if patching was the default

                                                                                                          4. 6

                                                                                                            What purpose does it serve that wouldn’t also be served by first-class rollback and an easier way of collapsing changesets on their way upstream? I find that most of the benefits of smaller changesets disappear when they don’t have commit messages, and when using the staging area for this you can only rollback one step without having to get into the hairy parts of git.

                                                                                                            1. 3

                                                                                                              The staging area is difficult to work with until you understand what’s happening under the hood. In most version control systems, an object under version control would be in one of a handful of states: either the object has been cataloged and stored in its current state, or it hasn’t. From a DWIM standpoint for a new git user, would catalog and store the object in its current state. With the stage, you can stage, and change, stage again, and change again. I’ve used this myself to logically group commits so I agree with you that it’s useful. But I do see how it breaks peoples DWIM view on how git works.

                                                                                                              Also, If I stage, and then change, is there a way to have git restore the file as I staged it if I haven’t committed?

                                                                                                              1. 7

                                                                                                                Also, If I stage, and then change, is there a way to have git restore the file as I staged it if I haven’t committed?

                                                                                                                Git restore .

                                                                                                                1. 3

                                                                                                                  I’ve implemented git from scratch. I still find the staging area difficult to use effectively in practice.

                                                                                                                2. 1

                                                                                                                  Try testing your staged changes atomically before you commit. You can’t.

                                                                                                                  A better design would have been an easy way to unstage, similar to git stash but with range support.

                                                                                                                  1. 5

                                                                                                                    You mean git stash --keep-index?

                                                                                                                    1. 3

                                                                                                                      Interesting, that would solve the problem. I’m surprised I’ve not come across that before.

                                                                                                                      In terms of “what’s wrong with the staging area”, what I was suggesting would work better is to have the whole thing work in reverse. So all untracked files are “staged” by default and you would explicitly un-stage anything you don’t want to commit. Firstly this works better for the 90% use-case, and compared to this workaround it’s a single step rather than 2 steps for the 10% case where you don’t want to commit all your changes yet.

                                                                                                                      The fundamental problem with the staging area is that it’s an additional, hidden state that the final committed state has to pass through. But that means that your commits do not necessarily represent a state that the filesystem was previously in, which is supposed to be a fundamental guarantee. The fact that you have to explicitly stash anything to put the staging area into a knowable state is a bit of a hack. It solves a problem that shouldn’t exist.

                                                                                                                      1. 2

                                                                                                                        The way I was taught this, the way I’ve taught this to others, and the way it’s represented in at least some guis is not compatible.

                                                                                                                        I mean, sure, you can have staged and unstaged changes in a file and need to figure it out for testing, or unstage parts, but mostly it’s edit -> stage -> commit -> push.

                                                                                                                        That feels, to me and to newbies who barely know what version control is, like a logical additive flow. Tons of cases you stage everything and commit so it’s a very small operation.

                                                                                                                        The biggest gripe may be devs who forget to add files in the proper commit, which makes bisect hard. Your case may solve that for sure, but I find it a special case of bad guis and sloppy devs who do that. Also at some point the fs layout gets fewer new files.

                                                                                                                        1. 2

                                                                                                                          Except that in a completely linear flow the distinction between edit and stage serves no purpose. At best it creates an extra step for no reason and at worst it is confusing and/or dangerous to anyone who doesn’t fully understand the state their working copy is in. You can bypass the middle state with git add .; git commit and a lot of new developers do exactly that, but all that does is pretend the staging state doesn’t exist.

                                                                                                                          Staging would serve a purpose if it meant something similar to pushing a branch to CI before a merge, where you have isolated the branch state and can be assured that it has passed all required tests before it goes anywhere permanent. But the staging area actually does the opposite of that, by creating a hidden state that cannot be tested directly.

                                                                                                                          As you say, all it takes is one mistake and you end up with a bad commit that breaks bisect later. That’s not just a problem of developers being forgetful, it’s the bad design of the staging area that makes this likely to happen by default.

                                                                                                                          1. 1

                                                                                                                            I think I sort of agree but do not completely concur.

                                                                                                                            Glossing over the staging can be fine in some projects and dev sloppiness is IMO a bigger problem than an additive flow for clean commits.

                                                                                                                            These are societal per-project issues - what’s the practice or policy or mandate - and thus they could be upheld by anything, even using the undo buffer for clean commits like back in the day. Which isn’t to say you never gotta do trickery like that with Git, just that it’s a flow that feels natural and undo trickery less common.

                                                                                                                            Skimming the other comments, maybe jj is more like your suggestion, and I wouldn’t mind “a better Git”, but I can’t be bothered when eg. gitless iirc dropped the staging and would make clean commits feel like 2003.

                                                                                                                    2. 2

                                                                                                                      If git stash --keep-index doesn’t do what you want the you could help further the conversation by elaborating on what you want.

                                                                                                                      1. 1
                                                                                                                    3. 16

                                                                                                                      The underlying data model however is pretty good. We can probably ditch the staging area,

                                                                                                                      Absolutely not. The staging area was a godsend coming from Subversion – it’s my favorite part of git bar none.

                                                                                                                      1. 4

                                                                                                                        Everyone seem to suppose I would like to ditch the workflows enabled by the staging area. I really don’t. I’m quite sure there ways to keep those workflows without using a staging area. If there aren’t well… I can always admit I was wrong.

                                                                                                                        1. 9

                                                                                                                          Well, what I prize being able to do is to build up a commit piecemeal out of some but not all of the changes in my working directory, in an incremental rather than all-in-one-go fashion (ie. I should be able to form the commit over time and I should be able to modify a file, move it’s state into the “pending commit” and continue to modify the file further without impacting the pending commit). It must be possible for any commit coming out of this workflow to both not contain everything in my working area, and to contain things no longer in my working area. It must be possible to diff my working area against the pending commit and against the last actual commit (separately), and to diff the pending commit against the last actual commit.

                                                                                                                          You could call it something else if you wanted but a rose by any other name etc. A “staging area” is a supremely natural metaphor for what I want to work with in my workflow, so replacing it hardly seems desirable to me.

                                                                                                                          1. 2

                                                                                                                            How about making the pending commit an actual commit? And then adding the porcelain necessary to treat it like a staging area? Stuff like git commit -p foo if you want to add changes piecemeal.

                                                                                                                            1. 11

                                                                                                                              No. That’s cool too and is what tools like git revise and git absorb enable, but making it an actual commit would have other drawbacks: it would imply it has a commit message and passes pre-commit hooks and things like that. The staging area is useful precisely for what it does now—help you build up the pieces necessary to make a commit. As such it implies you don’t have everything together to make a commit out of it. As soon as I do I commit, then if necessary --ammend, --edit, or git revise later. If you don’t make use of workflows that use staging then feel free to use tooling that bypasses it for you, but don’t try to take it away from the rest of us.

                                                                                                                              1. 9

                                                                                                                                pre-commit hooks

                                                                                                                                Oh, totally missed that one. Probably because I’ve never used it (instead i rely on CI or manually pushing a button). Still, that’s the strongest argument so far, and I have no good solution that doesn’t involve an actual staging area there. I guess it’s time to change my mind.

                                                                                                                                1. 2

                                                                                                                                  I think the final word is not said. These tools could also run hooks. It may be that new hooks need to be defined.

                                                                                                                                  Here is one feature request: run git hooks on new commit

                                                                                                                                  1. 1

                                                                                                                                    I think you missed the point, my argument is that the staging area is useful as a place to stage stuff before things like commit related hooks get run. I don’t want tools like git revise to run precommit hooks. When I use git revise the commit has already been made and presumably passed precommit phase.

                                                                                                                                    1. 1

                                                                                                                                      For the problem that git revise “bypasses” the commit hook when using it to split a commit, I meant the commit hook (not precommit hook).

                                                                                                                                      I get that the staging area lets you assemble a commit before you can run the commit hook. But if this was possible to do statelessly (which would only be an improvement), you could do without it. And for other reasons, git would be so much better without this footgun:

                                                                                                                                      Normally, you can look at git diff and commit what you see with git commit -a. But if the staging area is clobbered, which you might have forgot, you also have invisible state that sneaks in!

                                                                                                                                      1. 1

                                                                                                                                        Normally, you can look at git diff and commit what you see with git commit -a.

                                                                                                                                        Normally I do nothing of the kind. I might have used git commit -a a couple times in the last 5 years (and I make dozens to hundreds of commits per day). The stattefullness of the staging area is exactly what benefits my workflow and not the part I would be trying to eliminate. The majority of the time I stage things I’m working on from my editor one hunk at a time. The difference between my current buffer and the last git commit is highlighted and after I make some progress I start adding related hunks and shaping them into commits. I might fiddle around with a couple things in the current file, then when I like it stage up pieces into a couple different commits.

                                                                                                                                        The most aggressive I’d get is occasionally (once a month?) coming up with a use for git commit -u.

                                                                                                                                        A stateless version of staging that “lets you assemble a commit” sounds like an oxymoron to me. I have no idea what you think that would even look like, but a state that is neither the full contents of the current file system nor yet a commit is exactly what I want.

                                                                                                                                  2. 1

                                                                                                                                    Why not allow an empty commit message, and skip the commit hooks if a message hasn’t been set yet?

                                                                                                                                    1. 1

                                                                                                                                      Why deliberately make a mess of things? Why make a discreet concept of a “commit” into something else with multiple possible states? Why not just use staging like it is now? I see no benefit to jurry rigging more states on top of a working one. If the point is to simplify the tooling you won’t get there by overloading one clean concept with an indefinite state and contextual markers like “if commit message empty then this is not a real commit”.

                                                                                                                                      1. 1

                                                                                                                                        Empty commit message is how you abort a commit

                                                                                                                                        1. 1

                                                                                                                                          With the current UI.

                                                                                                                                          When discussing changes, there’s the possibility of things changing.

                                                                                                                                    2. 5

                                                                                                                                      Again, what’s the benefit?

                                                                                                                                      Sure, you could awkwardly simulate a staging area like this. The porcelain would have to juggle a whole bunch of shit to avoid breaking anytime you merge a bunch of changes after adding something to the fake “stage”, pull in 300 new commits, and then decide you want to unstage something, so the replacement of the dedicated abstraction seems likely to leak and introduce merge conflict resolution where you didn’t previously have to worry about it, but maybe with enough magic you could do it.

                                                                                                                                      But what’s the point? To me it’s like saying that I could awkwardly simulate if, while and for with goto, or simulate basically everything with enough NANDs. You’re not wrong, but what’s in it for me? Why am I supposed to like this any better than having a variety of fit-for-purpose abstractions? It just feels like I’d be tying one hand behind my back so there can be one less abstraction, without explain why having N-1 abstractions is even more desirable than having N.

                                                                                                                                      Seems like an “a foolish consistency is the hobgoblin of little minds” desire than anything beneficial, really.

                                                                                                                                      1. 1

                                                                                                                                        Again, what’s the benefit?

                                                                                                                                        Simplicity of implementation. Implementing the staging area like a commit, or at least like a pointer to a tree object, would likely make the underlying data model simpler. I wonder why the staging area was implemented the way it is.

                                                                                                                                        At the interface level however I’ve had to change my mind because of pre-commit hooks. When all you have is commits, and some tests are automatically launched every time you commit anything, it’s pretty hard to add stuff piecemeal.

                                                                                                                                        1. 3

                                                                                                                                          Yes, simplicity of implementation and UI. https://github.com/martinvonz/jj (mentioned in the article) makes the working copy (not the staging area) an actual commit. That does make the implementation quite a lot simpler. You also get backups of the working copy that way.

                                                                                                                                          1. 1

                                                                                                                                            Simplicity of implementation.

                                                                                                                                            No offence but, why would I give a shit about this? git is a tool I use to enable me to get other work done, it’s not something I’m reimplementing. If “making the implementation simpler” means my day-to-day workflows get materially more unpleasant, the simplicity of the implementation can take a long walk off a short pier for all I care.

                                                                                                                                            It’s not just pre-commit hooks that get materially worse with this. “Staging” something would then have to have a commit message, I would effectively have to branch off of head before doing every single “staging” commit in order to be able to still merge another branch and then rebase it back on top of everything without fucking about in the reflog to move my now-burried-in-the-past stage commit forward, etc, etc. “It would make the implementation simpler” would be a really poor excuse for a user hostile change.

                                                                                                                                            1. 3

                                                                                                                                              If “making the implementation simpler” means my day-to-day workflows get materially more unpleasant, the simplicity of the implementation can take a long walk off a short pier for all I care.

                                                                                                                                              I agree. Users shouldn’t have to care about the implementation (except for minor effects like a simpler implementation resulting in fewer bugs). But I don’t understand why your workflows would be materially more unpleasant. I think they would actually be more pleasant. Mercurial users very rarely miss the staging area. I was a git developer (mostly working on git rebase) a long time ago, so I consider myself a (former) git power user. I never miss the staging area when I use Mercurial.

                                                                                                                                              “Staging” something would then have to have a commit message

                                                                                                                                              Why? I think the topic of this thread is about what can be done differently, so why would the new tool require a commit message? I agree that it’s useful if the tool lets you provide a message, but I don’t think it needs to be required.

                                                                                                                                              I would effectively have to branch off of head before doing every single “staging” commit in order to be able to still merge another branch and then rebase it back on top of everything without fucking about in the reflog to move my now-burried-in-the-past stage commit forward

                                                                                                                                              I don’t follow. Are you saying you’re currently doing the following?

                                                                                                                                              git add -p
                                                                                                                                              git merge <another branch>
                                                                                                                                              git rebase <another branch>
                                                                                                                                              

                                                                                                                                              I don’t see why the new tool would bury the staging commit in the past. That’s not what happens with Jujutsu/jj anyway. Since the working copy is just like any other commit there, you can simply merge the other branch with it and then rebase the whole stack onto the other branch after.

                                                                                                                                              I’ve tried to explain a bit about this at https://github.com/martinvonz/jj/blob/main/docs/git-comparison.md#the-index. Does that help clarify?

                                                                                                                                              1. 1

                                                                                                                                                Mercurial users very rarely miss the staging area.

                                                                                                                                                Well, I’m not them. As somebody who was forced to use Mercurial for a bit and hated every second of it, I missed the hell out of it, personally (and if memory serves, there was later at least one inevitably-nonstandard Mercurial plugin to paper over this weakness, so I don’t think I was the only person missing it).

                                                                                                                                                I’ve talked about my workflow elsewhere in this thread, I’m not really interested in rehashing it, but suffice to say I lean on the index for all kinds of things.

                                                                                                                                                Are you saying you’re currently doing the following? git add -p git merge

                                                                                                                                                I’m saying that any number of times I start putting together a commit by staging things on Friday afternoon, come back on Monday, pull in latest from main, and continue working on forming a commit.

                                                                                                                                                If I had to (manually, we’re discussing among other things the assertion that you could eliminate the stage because it’s pointless, and you could “just” commit whenever you want to stage and revert the commit whenever they want to unstage ) commit things on Friday, forget I’d done so on Monday, pull in 300 commits from main, and then whoops I want to revert a commit 301 commits back so now I get to back out the merge and etc etc, this is all just a giant pain in the ass to even type out.

                                                                                                                                                Does that help clarify?

                                                                                                                                                I’m honestly not interested in reading it, or in what “Jujutsu” does, as I’m really happy with git and totally uninterested in replacing it. All I was discussing in this thread with Loup-Vaillant was the usefulness of the stage as an abstraction and my disinterest in seeing it removed under an attitude of “well you could just manually make commits when you would want to stage things, instead”.

                                                                                                                                                1. 2

                                                                                                                                                  I’m honestly not interested in reading it, or in what “Jujutsu” does

                                                                                                                                                  Too bad, this link you’re refusing to read is highly relevant to this thread. Here’s a teaser:

                                                                                                                                                  As a Git power-user, you may think that you need the power of the index to commit only part of the working copy. However, Jujutsu provides commands for more directly achieving most use cases you’re used to using Git’s index for.

                                                                                                                                                  1. 0

                                                                                                                                                    What “jujutsu” does under the hood has nothing whatsoever to do with this asinine claim of yours, which is the scenario I was objecting to: https://lobste.rs/s/yi97jn/is_it_time_look_past_git#c_k6w2ut

                                                                                                                                                    At this point I’ve had enough of you showing up in my inbox with these poorly informed, bad faith responses. Enough.

                                                                                                                                                    1. 1

                                                                                                                                                      I was claiming that the workflows we have with the staging area, we could achieve without. And Jujutsu here has ways to do exactly that. It has everything to do with the scenario you were objecting to.

                                                                                                                                                      Also, this page (and what I cited specifically) is not about what jujutsu does under the hood, it’s about its user interface.

                                                                                                                                                      1. 1

                                                                                                                                                        I’ve made it clear that I’m tired of interacting with you. Enough already.

                                                                                                                                              2. 1

                                                                                                                                                No offence but, why would I give a shit about [simplicity of implementation]?

                                                                                                                                                It’s because people don’t give a shit that we have bloated (and often slow) software.

                                                                                                                                                1. 0

                                                                                                                                                  And it’s because of developers with their heads stuck so far up their asses that they prioritize their implementation simplicity over the user experience that so much software is actively user-hostile.

                                                                                                                                                  Let’s end this little interaction here, shall we.

                                                                                                                                  3. 15

                                                                                                                                    Sublime Merge is the ideal git client for me. It doesn’t pretend it’s not git like all other GUI clients I’ve used so you don’t have to learn something new and you don’t unlearn git. It uses simple git commands and shows them to you. Most of git’s day-to-day problems go away if you can just see what you’re doing (including what you’ve mentioned).

                                                                                                                                    CLI doesn’t cut it for projects of today’s size. A new git won’t fix that. The state of a repository doesn’t fit in a terminal and it doesn’t fit in my brain. Sublime Merge shows it just right.

                                                                                                                                    1. 5

                                                                                                                                      I like GitUp for the same reasons. Just let me see what I’m doing… and Undo! Since it’s free, it’s easy to get coworkers to try it.

                                                                                                                                      1. 4

                                                                                                                                        I didn’t know about GitUp but I have become a big fan of gitui as of late.

                                                                                                                                        1. 2

                                                                                                                                          I’ll check that out, thank you!

                                                                                                                                      2. 2

                                                                                                                                        I use Fork for the same purpose and the staging area has never been a problem since it is visible and diffable at any time, and that’s how you compose your commits.

                                                                                                                                      3. 6

                                                                                                                                        See Game of Trees for an alternative to the git tool that interacts with normal git repositories.

                                                                                                                                        Have to agree with others about the value of the staging area though! It’s the One Big Thing I missed while using Mercurial.

                                                                                                                                        1. 5

                                                                                                                                          Well, on the one hand people could long for a better way to store the conflict resolutions to reuse them better on future merges.

                                                                                                                                          On the other hand, of all approaches to DAG-of-commits, Git’s model is plain worse than the older/parallel ones. Git is basically intended to lose valuable information about intent. The original target branch of the commit often tells as much as the commit message… but it is only available in reflog… auto-GCed and impossible to sync.

                                                                                                                                          1. 10

                                                                                                                                            Half of my branches are called werwerdsdffsd. I absolutely don’t want them permanently burned in the history. These scars from work-in-progress annoyed me in Mercurial.

                                                                                                                                            1. 9

                                                                                                                                              Honestly I have completely the opposite feeling. Back in the days before git crushed the world, I used Mercurial quite a lot and I liked that Mercurial had both the ephemeral “throw away after use” model (bookmarks) and the permanent-part-of-your-repository-history model (branches). They serve different purposes, and both are useful and important to have. Git only has one and mostly likes to pretend that the other is awful and horrible and nobody should ever want it, but any long-lived project is going to end up with major refactoring or rewrites or big integrations that they’ll want to keep some kind of “here’s how we did it” record to easily point to, and that’s precisely where the heavyweight branch shines.

                                                                                                                                              And apparently I wrote this same argument in more detail around 12 years ago.

                                                                                                                                              1. 1

                                                                                                                                                ffs_please_stop_refactoring_and_review_this_pr8

                                                                                                                                              2. 2

                                                                                                                                                This is a very good point. It would be interesting to tag and attach information to a group of related commits. I’m curious of the linux kernel workflows. If everything is an emailed patch, maybe features are done one commit at a time.

                                                                                                                                                1. 2

                                                                                                                                                  If you go further, there are many directions to extend what you can store and query in the repository! And of course they are useful. But even the data Git forces you to have (unlike, by the way, many other DVCSes where if you do not want a meaningful name you can just have multiple heads in parallel inside a branch) could be used better.

                                                                                                                                                2. 2

                                                                                                                                                  I can’t imagine a scenario where the original branch point of a feature would ever matter, but I am constantly sifting through untidy merge histories that obscure the intent.

                                                                                                                                                  Tending to your commit history with intentionality communicates to reviewers what is important, and removes what isn’t.

                                                                                                                                                  1. 1

                                                                                                                                                    It is not about the point a branch started from. It is about which of the recurring branches the commit was in. Was it in quick-fix-train branch or in update-major-dependency-X branch?

                                                                                                                                                    1. 2

                                                                                                                                                      The reason why this isn’t common is because of GitHub more than Git. They don’t provide a way to use merge commits that isn’t a nightmare.

                                                                                                                                                      When I was release managing by hand, my preferred approach was rebasing the branch off HEAD but retaining the merge commit, so that the branch commits were visually grouped together and the branch name was retained in the history. Git can do this easily.

                                                                                                                                                3. 5

                                                                                                                                                  I never understood the hate for Git’s CLI. You can learn 99% of what you need to know on a daily basis in a few hours. That’s not a bad time investment for a pivotal tool that you use multiple times every day. I don’t expect a daily driver tool to be intuitive, I expect it to be rock-solid, predictable, and powerful.

                                                                                                                                                  1. 9

                                                                                                                                                    This is a false dichotomy: it can be both (as Mercurial is). Moreover, while it’s true that you can learn the basics to get by with in a few hours, it causes constant low-level mental overhead to remember how different commands interact, what the flag is in this command vs. that command, etc.—and never mind that the man pages are all written for people thinking in terms of the internals, instead of for general users. (That this is a common failing of man pages does not make it any less a problem for git!)

                                                                                                                                                    One way of saying it: git has effectively zero progressive disclosure of complexity. That makes it a continual source of paper cuts at minimum unless you’ve managed to actually fully internalize not only a correct mental model for it but in many cases the actual implementation mechanics on which it works.

                                                                                                                                                    1. 3

                                                                                                                                                      Its manpages are worthy of a parody: https://git-man-page-generator.lokaltog.net

                                                                                                                                                    2. 2

                                                                                                                                                      Its predecessors CVS and svn had much more intuitive commands (even if they were was clumsy to use in other ways). DARCS has been mentioned many times as being much more easy to use as well. People migrating from those tools really had a hard time, especially because git changed the meanings of some commands, like checkout.

                                                                                                                                                      Then there were some other tools that came up around the same time or shortly after git but didn’t get the popularity of git like hg and bzr, which were much more pleasant to use as well.

                                                                                                                                                      1. 2

                                                                                                                                                        I think the issues people have are less about the CLI itself and more about how it interfaces with the (for some developers) complex and hard to understand concepts at hand.

                                                                                                                                                        Take rebase for example. Once you grok what it is, it’s easy, but trying to explain the concept of replaying commits on top of others to someone used to old school tools like CVS or Subversion can be a challenge, especially when they REALLY DEEPLY don’t care and see this as an impediment to getting their work done.

                                                                                                                                                        I’m a former release engineer, so I see the value in the magic Git brings to the table, but it can be a harder sell for some :)

                                                                                                                                                      2. 5

                                                                                                                                                        The interface is pretty bad.

                                                                                                                                                        I would argue that this is one of the main reasons for git’s success. The CLI is so bad that people were motivated to look for tools to avoid using it. Some of them were motivated to write tools to avoid using it. There’s a much richer set of local GUI and web tools than I’ve seen for any other revision control system and this was true even when git was still quite new.

                                                                                                                                                        I never used a GUI with CVS or Subversion, but I wanted to as soon as I started touching the git command line. I wanted features like PRs and web-based code review, because I didn’t want to merge things locally. I’ve subsequently learned a lot about how to use the git CLI and tend to use it for a lot of tasks. If it had been as good as, say, Mercurial’s from the start then I never would have adopted things like gitx / gitg and GitHub and it’s those things that make the git ecosystem a pleasant place to be.

                                                                                                                                                        1. 4

                                                                                                                                                          The interface of Git and its underlying data models are two very different things, that are best treated separately.

                                                                                                                                                          Yes a thousand times this! :) Git’s data model has been a quantum leap for people who need to manage source code at scale. Speaking as a former release engineer, I used to be the poor schmoe who used to have to conduct Merge Day, where a branch gets merged back to main.

                                                                                                                                                          There was exactly one thing you could always guarantee about merge day: There Will Be Blood.

                                                                                                                                                          So let’s talk about looking past git’s god awful interface, but keep the amazing nubbins intact and doing the nearly miraculous work they do so well :)

                                                                                                                                                          And I don’t just mean throwing a GUI on top either. Let’s rethink the platonic ideal for how developers would want their workflow to look in 2022. Focus on the common case. Let the ascetics floating on a cloud of pure intellect script their perfect custom solutions, but make life better for the “cold dark matter” developers which are legion.

                                                                                                                                                          1. 2

                                                                                                                                                            I would say that you simultaneously give credit where it is not due (there were multiple DVCSes before Git, and approximately every one had a better data model, and then there are things that Subversion still has better than everyone else, somehow), and ignore the part that actually made your life easier — the efforts of pushing Git down people’s throat, done by Linus Torvalds, spending orders of magnitude more of his time on this than on getting things right beyond basic workability in Git.

                                                                                                                                                            1. 1

                                                                                                                                                              Not a DVCS expert here, so would you please consider enlightening me? Which earlier DVCS were forgotten?

                                                                                                                                                              My impressions of Mercurial and Bazaar are that they were SL-O-O-W, but they’re just anecdotal impressions.

                                                                                                                                                              1. 3

                                                                                                                                                                Well, Bazaar is technically earlies. Monotone is significantly earlier. Monotone has quite interesting and nicely decoupled data model where the commit DAG is just one thing; changelog, author — and branches get the same treatment — are not parts of a commit, but separately stored claims about a commit, and this claim system is extensible and queriable. And of course Git was about Linus Torvalds speedrunning implementation of the parts of BitKeeper he really really needed.

                                                                                                                                                                It might be that in the old days running on Python limited speed of both Mercurial and Bazaar. Rumour has it that the Monotone version Torvalds found too slow was indeed a performance regression (they had one particularly slow release at around that time; Monotone is not in Python)

                                                                                                                                                                Note that one part of things making Git fast is that enables some optimisations that systems like Monotone make optional (it is quite optimistic about how quickly you can decide that the file must not have been modified, for example). Another is that it was originally only intended to be FS-safe on ext3… and then everyone forgot to care, so now it is quite likely to break the repository in case of unclean shutdown mid-operation. Yes, I have damaged repositories that way to a state where I could not find advice on how to avoid re-cloning to get even partially working repository.

                                                                                                                                                                As of Subversion, it has narrow checkouts which are a great feature, and DVCSes could also have them, but I don’t think anyone properly has them. You kind of can hack something with remote-automate in Monotone, but probably flakily.

                                                                                                                                                          2. 4

                                                                                                                                                            Let the data model pretend there’s a blob for each version of that huge file, even though in fact the software is automatically compressing & decompressing things under the hood.

                                                                                                                                                            Ironically, that’s part of the performance problem – compressing the packfiles tends to be where things hurt.

                                                                                                                                                            Still, this is definitely a solvable problem.

                                                                                                                                                            1. 2

                                                                                                                                                              I used to love DARCS, but I think patch theory was probably the wrong choice.

                                                                                                                                                              I have created and maintains official test suite for pijul, i am the happiest user ever.

                                                                                                                                                              1. 2

                                                                                                                                                                Hmm, knowing you I’m sure you’ve tested it to death.

                                                                                                                                                                I guess they got rid of the exponential conflict resolution that plagued DARCS? If so perhaps I should give patch theory another go. Git ended up winning the war before I got around to actually study patch theory, maybe it is sounder than I thought.

                                                                                                                                                                1. 1

                                                                                                                                                                  Pijul is a completely different thing than Darcs, the current state of a repository in Pijul is actually a special instance of a CRDT, which is exactly what you want for a version control system.

                                                                                                                                                                  Git is also a CRDT, but HEAD isn’t (unlike in Pijul), the CRDT in Git is the entire history, and that is not a very useful property.

                                                                                                                                                                2. 1

                                                                                                                                                                  Best test suite ever. Thanks again, and again, and again for that. It also helped debug Sanakirja, a database engine used as the foundation of Pijul, but usable in other contexts.

                                                                                                                                                                3. 2

                                                                                                                                                                  There are git-compatible alternatives that keep the underlying model and change the interface. The most prominent of these is probably gitless.

                                                                                                                                                                  1. 1

                                                                                                                                                                    I’ve been using git entirely via UI because of that. Much better overview, much more intuitive, less unwanted side effects.

                                                                                                                                                                    1. 1

                                                                                                                                                                      You can’t describe Git without discussing rebase and merge: these are the two most common operations in Git, yet they don’t satisfy any interesting mathematical property such as associativity or symmetry:

                                                                                                                                                                      • Associativity is when you want to merge your commits one by one from a remote branch. This should intuitively be the same as merging the remote HEAD, but Git manages to make it different sometimes. When that happens, your lines can be shuffled around more or less randomly.

                                                                                                                                                                      • Symmetry means that merging A and B is the same as merging B and A. Two coauthors doing the same conflictless merge might end up with different results. This is one of the main benefits of GitHub: merges are never done concurrently when you use a central server.

                                                                                                                                                                      1. 1

                                                                                                                                                                        Well, at least this is not the fault of the data model: if you have all the snapshots, you can deduce all the patches. It’s the operations themselves that need fixing.

                                                                                                                                                                        1. 1

                                                                                                                                                                          My point is that this is a common misconception: no datastructure is ever relevant without considering the common operations we want to run on it.

                                                                                                                                                                          For Git repos, you can deduce all the patches indeed, but merge and rebase can’t be fixed while keeping a reasonable performance, since the merge problem Git tries to solve is the wrong one (“merge the HEADs, knowing their youngest common ancestor”). That problem cannot have enough information to satisfy basic intuitive properties.

                                                                                                                                                                          The only way to fix it is to fetch the entire sequence of commits from the common ancestor. This is certainly doable in Git, but merges become O(n) in time complexity, where n is the size of history.

                                                                                                                                                                          The good news is, this is possible. The price to pay is a slightly more complex datastructure, slightly harder to implement (but manageable). Obviously, the downside is that it can’t be consistent with Git, since we need more information. On the bright side, it’s been implemented: https://pijul.org

                                                                                                                                                                          1. 1

                                                                                                                                                                            no datastructure is ever relevant without considering the common operations we want to run on it.

                                                                                                                                                                            Agreed. Now, how often do we actually merge stuff, and how far is the common ancestor in practice?

                                                                                                                                                                            My understanding of the usage of version control is that merging two big branches (with an old common ancestor) is rare. Far more often we merge (or rebase) work units with just a couple commits. Even more often than that we have one commit that’s late, so we just pull in the latest change then merge or rebase that one commit. And there are the checkout operations, which in some cases can occur most frequently. While a patch model would no doubt facilitate merges, it may not be worth the cost of making other, arguably more frequent operations, slower.

                                                                                                                                                                            (Of course, my argument is moot until we actually measure. But remember that Git won in no small part because of its performance.)

                                                                                                                                                                            1. 2

                                                                                                                                                                              I agree with all that, except that:

                                                                                                                                                                              • the only proper modelling of conflicts, merges and rebases/cherry-picking I know of (Pijul) can’t rely on common ancestors only, because rebases can make some future merges more complex than a simple 3-way merge problem.

                                                                                                                                                                              • I know many engineers are fascinated by Git’s speed, but the algorithm running on the CPU is almost never the bottleneck: the operator’s brain is usually much slower than the CPU in any modern version control system (even Darcs has fixed its exponential merge). Conflicts do happen, so do cherry-picks and rebases. They aren’t rare in large projects, and can be extremely confusing without proper tools. Making these algorithms fast is IMHO much more important from a cost perspective than gaining 10% on a operation already taking less than 0.1 second. I won’t deny the facts though: if Pijul isn’t used more in industry, it could be partly because that opinion isn’t widely shared.

                                                                                                                                                                              • some common algorithmic operations in Git are slower than in Pijul (pijul credit is much faster than git blame on large instances), and most operations are comparable in speed. One thing where Git is faster is browsing old history: the datastructures are ready in Pijul, but I haven’t implemented the operations yet (I promised I would do that as soon as this is needed by a real project).

                                                                                                                                                                    1. 2

                                                                                                                                                                      This seems fun, and I like many of the tech choices, but if doesn’t feel… Useful? Like, a grid of stuff that might of might not change? If it doesn’t change, why am I looking at it? If it does, why don’t I want history?

                                                                                                                                                                      1. 3

                                                                                                                                                                        From my read of the intro and the spec, there’s nothing to prevent the client from keeping track of state and/or history in a local context.

                                                                                                                                                                        1. 2

                                                                                                                                                                          I imagine you’d have a big enough grid such that some bits would always be changing on a daily or hourly basis. It’s something you could pop open for a brief info-snack, but the design discourages endless surfing/scrolling. (I was going to write “prevents”, but the items can have links and you can click them and wander off from there…)

                                                                                                                                                                          It also seems like a great application for an always-on ambient display in a spot that you walk past. Kind of a non-purposeful version of those greenboard displays teams put up in offices.

                                                                                                                                                                          1. 2

                                                                                                                                                                            I don’t get how this should not encourage me to refresh it all the time and manually look for updates?

                                                                                                                                                                            Maybe I’m the odd one out with my RSS-inbox-zero habits, but I really like that I can open my news reader, check everything from site X”, and mark all as read - done. This feels like “having to visit several times per day/week again, just the opposite of RSS and a state of “done”.

                                                                                                                                                                            1. 2

                                                                                                                                                                              This is a thing clients can implement differently. A client could have a “new boards” mode or filter, or show random boards you’re subscribed to algorithmically, or any other presentation design you can come up with which matches the spec (which, interestingly, implicitly excludes ‘crawling’: the spec draft forbids listing boards).

                                                                                                                                                                              1. 1

                                                                                                                                                                                Yeah, it’s a different mindset. I’m the same as you with RSS, but I think of this more like tumblr, which I can (could) visit periodically for a snack instead of feeling like I had to catch up on everything.

                                                                                                                                                                          1. 2

                                                                                                                                                                            I’m not sure Robin wants this shared on social media yet. It’s still in the ideation phase. See https://www.robinsloan.com/lab/specifying-spring-83/

                                                                                                                                                                            1. 4

                                                                                                                                                                              Exact quote for other folks (who should feel free to agree with Carl and disagree with my choice to share in a few places):

                                                                                                                                                                              It’s okay to share this link, but I want to underscore that I am sending it specifically to you with the hope that you will … really think about it! At such a primordial stage, a proposal like this doesn’t need diffuse, drive-by attention. It needs, instead, close consideration and generous imagination.

                                                                                                                                                                              I took putting it here as being in that spirit but could see it the other way too!

                                                                                                                                                                            1. 8

                                                                                                                                                                              Unlike most intentionally-limited protocols like Gemini, I quite like this one. The idea of a federated network of little rectangles appeals to me. And I love Curve25519 and key-based identity.

                                                                                                                                                                              I am a bit disappointed by the arbitrary(?) limit of 2217 bytes of HTML/CSS without external resources. I would really like to see images, and I think this size is too small to allow much, since you’d have to express the image in base64 as a data: URL. While I recognize that one wants to keep the size down to avoid servers getting bloated, surely we can handle somewhat bigger sizes. You can store a million of these snippets in 2GB, which is peanuts. Why not 22170 bytes, or 221700?

                                                                                                                                                                              1. 5

                                                                                                                                                                                Yeah, I am curious as well about why that specific size, but he actually does explicitly talk about the size limit later in the introduction, and it has two interesting consequences/relationships:

                                                                                                                                                                                1. You can put 10M of these in <23GB. One upside to that: the storage for it will fit in the limits of every $5/month VPS service out there today, which makes it super cheap to run your own server.
                                                                                                                                                                                2. It comes with an upper bound of 10M active boards, ever: a move somewhat inspired by (but very different in implications from!) the artificial scarcity of blockchains.

                                                                                                                                                                                The other thing I’ve been mulling on is: one of the wins from “microblogging” is the creativity inspired by its limits. Limits are provocations to be clever! (I think often of Craig Mod’s essay Let’s Talk About Margins, now nearly a decade old.) That dynamic is in play a bit here—unsurprisingly, as that dynamic is one of the themes of Robin’s work in general.

                                                                                                                                                                                1. 9

                                                                                                                                                                                  since a lot of implementations will probably store those snippets in individual files, you could raise the limit to 4096 bytes and not take up any more space :) Although you might have to subtract a few bytes for metadata.

                                                                                                                                                                                  1. 1

                                                                                                                                                                                    Can you really efficiently store 10M items as individual files on Linux (which will be the dominant server OS for this if it ever takes off)?

                                                                                                                                                                                    My initial thought would be a database. Most accesses would be read requests from clients anyway.

                                                                                                                                                                                    1. 1

                                                                                                                                                                                      I don’t know anything about the innards of Linux filesystems. I would hope that by today they’d be able to handle large directories efficiently, but you never know. A common workaround is to have two or three levels of hierarchy, e.g. 3000 directories each with 3000 files. That’s what Git does, for instance.

                                                                                                                                                                                      1. 1

                                                                                                                                                                                        Yeah, these are implementation details. But having one file per entry does lead to a lot of overhead if the byte limit is strictly enforced. I’d not be surprised if authoring clients did all sorts of minimization tricks to get as much into an entry as possible, and in that case servers can leverage that by storing the data in flat files for easy indexing and retrieval.

                                                                                                                                                                              1. 4
                                                                                                                                                                                • celebrating my daughters’ 10th and 8th birthdays
                                                                                                                                                                                • finishing preparing, and then actually delivering, a sermon—at a good friend’s church so he can have a week off of preaching
                                                                                                                                                                                • doing a bit of revision on the fanfare I wrote in 2020, in preparation for possibly hiring an orchestra to record it properly later this year (a thing I only recently learned you can do!)
                                                                                                                                                                                1. 1

                                                                                                                                                                                  Of course, I can make my own ZeroOrMoreVec<T> and OneOrMoreVec<T> types

                                                                                                                                                                                  My mind started to go there as soon as they introduced the problem of modeling a non-empty collection. And I seem to recall a language or framework that supports this — IIRC you could parameterize a collection by min and/or max size and the type system was pretty smart about tracking this. So for example Vec<Min,Max>.pop() returns Vec<Min-1,Max-1> and of course won’t compile if Min==0.

                                                                                                                                                                                  1. 2

                                                                                                                                                                                    Doing this in general (rather than special casing some collections or types in the compiler) is dependent types, which he alluded to at the end. This example is actually the canonical example of dependent types. As a name, “dependent types” doesn’t really convey what it is, though: it makes values into types. The “dependent” bit is from the idea that the type of a thing depends on its value. Here, for example, Vec<String, 5> (not real notation!) would be an item which has both the value that it has five items in it and a type that it has five items in it. That’s incredibly powerful, but it also comes with the cost of that much more type machinery to learn and maintain, and is (therefore?) very far from mainstream.

                                                                                                                                                                                  1. 8

                                                                                                                                                                                    I suppose it depends on the company, time, and luck, and “YMMV” as always. However, my experience working in staff roles was quite miserable, and many of my friends had the same experience.

                                                                                                                                                                                    Your manager may report to the COO (or the CEO in smaller companies), but it may not mean anything for either of you. If executives see you as a cost center that steals money from the real business, you will have to fight tooth and nail to keep your department funded. You may not even win: at quite a few places I’ve seen, such internal departments were staffed mainly by inexperienced people who would leave for a better job as soon as they could find one. But when disaster happens, you will be blamed for everything.

                                                                                                                                                                                    I’m pretty sure there are companies that don’t mistreat their staff IT personnel, but no assumption is universal.

                                                                                                                                                                                    1. 9

                                                                                                                                                                                      IME: the harder it is for execs to see that “person/group X does their job which directly leads to profit” the more of an uphill battle it is. Even a single hop can have a big effect: note the salary differences between skilled sales people and skilled engineers.

                                                                                                                                                                                      1. 5

                                                                                                                                                                                        Can confirm. This is particularly challenging for “developer experience” or “productivity” teams, where all of the work is definitionally only an indirect contribution to the bottom line—even if an incredibly important and valuable one.

                                                                                                                                                                                        1. 2

                                                                                                                                                                                          Gotta be able to sell everything you do. It’s hard when metrics are immaterial but in those specific areas, you have to be showing “oh, I save business line X this many person-hours daily/weekly/etc.” constantly in order to advance

                                                                                                                                                                                          1. 5

                                                                                                                                                                                            As an idea that sounds good, but in practice no one knows how to even estimate that in a lot of categories of important tech investment for teams like mine. I have spent a non-trivial amount of time with both academic and industrial literature on the subject, and… yeah, nobody blows how to measure or even guesstimate this stuff in a way that I could remotely sign my name to in good conscience.

                                                                                                                                                                                        2. 1

                                                                                                                                                                                          note the salary differences between skilled sales people and skilled engineers.

                                                                                                                                                                                          The latter usually have a higher salary or total compensation so I’m not sure if I understood your point. Maybe sales make more in down-market areas of the industry that don’t pay more than $100k for programmers if they can help it?

                                                                                                                                                                                          1. 5

                                                                                                                                                                                            $100k for programmers exists in the companies that have effectively scaled up their sales pipeline. Most programmers work on some kind of B2B software (like the example in the article, internal billing for an electricity company), where customers don’t number in the millions, engineer salaries have five digits, and trust me, their package can’t touch the compensation of the skilled sales person who manages the expectations of a few very important customers.

                                                                                                                                                                                            1. 3

                                                                                                                                                                                              I can confirm that I have never worked for companies where the sales people were paid less than the engineers. At least not to my knowledge.

                                                                                                                                                                                              In fact, in most companies I worked for, the person I reported to had a sales role.

                                                                                                                                                                                              1. 2

                                                                                                                                                                                                I think a good discriminant for this might be software-as-plumbing vs. software-is-the-product. I suspect SaaS has driven down the costs a lot of glue type stuff like this.

                                                                                                                                                                                          2. 5

                                                                                                                                                                                            I’ve had exactly the opposite experience. Being in staff roles has been the most enjoyable because we could work on things that had longer term payoffs. When I’ve been a line engineer we weren’t allowed to discuss anything unless it would increase revenue that quarter. The staff roles paid slightly less but not too much less.

                                                                                                                                                                                            1. 2

                                                                                                                                                                                              I had a similar experience. I worked on a devops team at a small startup, and we did such a good job that when covid hit and cuts needed to be made, our department was first on the chopping block. I landed on my feet just fine, finding a job that paid 75% more (and have since received a promotion and a couple of substantial raises), but I was surprised to learn that management may keep a floundering product/dev org over an excellent supporting department (even though our department could’ve transitioned to dev and done a much better job).

                                                                                                                                                                                            1. 12

                                                                                                                                                                                              The lesson here sounds more like “bad protocols will make your client/server system slow and clumsy”, not “move all of your system’s code to the server.” The OP even acknowledges that GraphQL would have helped a lot. (Or alternatively something like CouchDB’s map/reduce query API.)

                                                                                                                                                                                              I don’t really get the desire to avoid doing work on the client side. Your system includes a lot of generally-quite-fast CPUs provided for free by users, and the number of these scales 1::1 with the number of users. Why not offload work onto them from your limited and costly servers? Obviously you’re already using them for rendering, but you can move a lot of app logic there too.

                                                                                                                                                                                              I’m guessing that the importance of network protocol/API design has been underappreciated by web devs. REST is great architecturally but if you use it as a cookie-cutter approach it’s non-optimal for app use. GraphQL seems a big improvement.

                                                                                                                                                                                              1. 17

                                                                                                                                                                                                Your system includes a lot of generally-quite-fast CPUs provided for free by users

                                                                                                                                                                                                Yes, and if every site I’m visiting assumes that, then pretty quickly, I no longer have quite-fast CPUs to provide for free, as my laptop is slowly turning to slag due to the heat.

                                                                                                                                                                                                1. 8

                                                                                                                                                                                                  Um, no. How many pages are you rendering simultaneously?

                                                                                                                                                                                                  1. 3

                                                                                                                                                                                                    I usually have over 100 tabs open at any one time, so a lot.

                                                                                                                                                                                                    1. 5

                                                                                                                                                                                                      If your browser actually keeps all those tabs live and running, and those pages are using CPU cycles while idling in the background and the browser doesn’t throttle them, I can’t help you… ¯\_(ツ)_/¯

                                                                                                                                                                                                      (Me, I use Safari.)

                                                                                                                                                                                                      1. 3

                                                                                                                                                                                                        Yes, but assuming three monitors you likely have three, four windows open. That’s four active tabs, Chrome put the rest of them to sleep.

                                                                                                                                                                                                        And even if you only use apps like the one from the article, and not the well-developed ones like the comment above suggests, it’s maybe five of them at the same time. And you’re probably not clicking frantically all over them at once.

                                                                                                                                                                                                        1. 2

                                                                                                                                                                                                          All I know is that when my computer slows to a crawl the fix that usually works is to go through and close a bunch of Firefox tabs and windows.

                                                                                                                                                                                                          1. 4

                                                                                                                                                                                                            There is often one specific tab which for some reason is doing background work and ends up eating a lot of resources. When I find that one tab and close it my system goes back to normal. Like @zladuric says, browsers these days don’t let inactive tabs munch resources.

                                                                                                                                                                                                  2. 8

                                                                                                                                                                                                    I don’t really get the desire to avoid doing work on the client side.

                                                                                                                                                                                                    My understanding is that it’s the desire to avoid some work entirely. If you chop up the processing so that the client can do part of it, that carries its own overhead. How do you feel about this list?

                                                                                                                                                                                                    Building a page server-side:

                                                                                                                                                                                                    • Server: Receive page request
                                                                                                                                                                                                    • Server: Query db
                                                                                                                                                                                                    • Server: Render template
                                                                                                                                                                                                    • Server: Send page
                                                                                                                                                                                                    • Client: Receive page, render HTML

                                                                                                                                                                                                    Building a page client-side:

                                                                                                                                                                                                    • Server: Receive page request
                                                                                                                                                                                                    • Server: Send page (assuming JS is in-page. If it isn’t, add ‘client requests & server sends the JS’ to this list.)
                                                                                                                                                                                                    • Client: Receive page, render HTML (skeleton), interpret JS
                                                                                                                                                                                                    • Client: Request data
                                                                                                                                                                                                    • Server: Receive data request, query db
                                                                                                                                                                                                    • Server: Serialize data (usu. to JSON)
                                                                                                                                                                                                    • Server: Send data
                                                                                                                                                                                                    • Client: Receive data, deserialize data
                                                                                                                                                                                                    • Client: Build HTML
                                                                                                                                                                                                    • Client: Render HTML (content)

                                                                                                                                                                                                    Compare the paper Scalabiilty! But at what COST!, which found that the overhead of many parallel processing systems gave them a high “Configuration to Outperform Single Thread”.

                                                                                                                                                                                                    1. 4

                                                                                                                                                                                                      That’s an accurate list… for the first load! One attraction of doing a lot more client-side is that after the first load, the server had the same list of actions for everything you might want to do, while the client side looks more like:

                                                                                                                                                                                                      • fetch some data
                                                                                                                                                                                                      • deserialize it
                                                                                                                                                                                                      • do an in-place rerender, often much smaller than a full page load

                                                                                                                                                                                                      (Edit: on rereading your post your summary actually covers all requests, but missed how the request and response and client-side rerender can be much smaller this way. But credit where due!)

                                                                                                                                                                                                      That’s not even getting at how much easier it is to do slick transitions or to maintain application state correctly across page transitions. Client side JS state management takes a lot of crap and people claim solutions like these are simpler but… in practice many of the sites which use them have very annoying client side state weirdness because it’s actually hard to keep things in sync unless you do the full page reload. (Looking at you, GitHub.)

                                                                                                                                                                                                      1. 6

                                                                                                                                                                                                        When I’m browsing on mobile devices I rarely spend enough time on any single site for the performance benefits of a heavy initial load to kick in.

                                                                                                                                                                                                        Most of my visits are one page long - so I often end up loading heavy SPAs when a lighter, single page optimized to load fast from an un sched blank state would have served me much better.

                                                                                                                                                                                                        1. 4

                                                                                                                                                                                                          I would acknowledge that this is possible.

                                                                                                                                                                                                          But that’s almost exactly what the top comment said. People use framework of the day for a blog. Not flattening it, or remixing it or whatever.

                                                                                                                                                                                                          SPAs that I use are things like Twitter, the tab is likely always there. (And on desktop i have those CPU cores.)

                                                                                                                                                                                                          It’s like saying, I only ride on trains to work, and they’re always crowded, so trains are bad. Don’t use trains if your work is 10 minutes away.

                                                                                                                                                                                                          But as said, I acknowledge that people are building apps where they should be building sites. And we suffer as the result.

                                                                                                                                                                                                          What still irks me the most are sites with a ton of JavaScript. So it’s server-rendered, it just has a bunch of client-side JavaScript that’s unused, or loading images or ads or something.

                                                                                                                                                                                                      2. 4

                                                                                                                                                                                                        You’re ignoring a bunch of constant factors. The amount of rendering to create a small change on the page is vastly smaller than that to render a whole new page. The most optimal approach is to send only the necessary data over the network to create an incremental change. That’s how native client/server apps work.

                                                                                                                                                                                                        1. 5

                                                                                                                                                                                                          In theory yes but if in practice if the “most optimal approach” requires megabytes of JS code to be transmitted, parsed, executed through 4 levels of interpreters culminating in JIT compiling the code to native machine code all the while performing millions of checks to make sure that this complex system doesn’t result in a weird machine that can take over your computer then maybe sending a “whole new page” consisting of 200 kb of static HTML upon submitting a form would be more optimal.

                                                                                                                                                                                                          1. 4

                                                                                                                                                                                                            In theory yes but if in practice if the “most optimal approach” requires megabytes of JS code to be transmitted, parsed, executed through 4 levels of interpreters culminating in JIT compiling the code to native machine code all the while performing millions of checks to make sure that this complex system doesn’t result in a weird machine that can take over your computer

                                                                                                                                                                                                            This is hyperbole. Sending a ‘“whole new page” of 200 kb of static HTML’ has your userspace program block on the kernel as bytes are written into some socket buffer, NIC interrupts the OS to grab these bytes, the NIC generates packets containing the data, userspace control is then handed back to the app which waits until the OS notifies it that there’s data to read, and on and on. I can do this for anything on a non-embedded computer made in the last decade.

                                                                                                                                                                                                            Going into detail for dramatic effect doesn’t engage with the original argument nor does it elucidate the situation. Client-side rendering makes you pay a one-time cost for consuming more CPU time and potentially more network bandwidth for less incremental CPU and bandwidth. That’s all. Making the tradeoff wisely is what matters. If I’m loading a huge Reddit or HN thread for example, it might make more sense to load some JS on the page and have it adaptively load comments as I scroll or request more content. I’ve fetched large threads on these sites from their APIs before and they can get as large as 3-4 MB when rendered as a static HTML page. Grab four of these threads and you’re looking at 12-16 MB. If I can pay a bit more on page load then I can end up transiting a lot less bandwidth through adaptive content fetching.

                                                                                                                                                                                                            If, on the other hand, I’m viewing a small thread with a few comments, then there’s no point paying that cost. Weighing this tradeoff is key. On a mostly-text blog where you’re generating kB of content, client-side rendering is probably silly and adds more complexity, CPU, and bandwidth for little gain. If I’m viewing a Jupyter-style notebook with many plots, it probably makes more sense for me to be able to choose which pieces of content I fetch to not fetch multiple MB of content. Most cases will probably fit between these two.

                                                                                                                                                                                                            Exploring the tradeoffs in this space (full React-style SPA, HTMX, full SSR) can help you come to a clean solution for your usecase.

                                                                                                                                                                                                            1. 1

                                                                                                                                                                                                              I was talking about the additional overhead required to achieve “sending only the necessary data over the network”.

                                                                                                                                                                                                      3. 4

                                                                                                                                                                                                        I don’t really get the desire to avoid doing work on the client side.

                                                                                                                                                                                                        My impression is that it is largely (1) to avoid JavaScript ecosystem and/or* (2) avoid splitting app logic in half/duplicating app logic. Ultimately, your validation needs to exist on the server too because you can’t trust clients. As a rule of thumb, SSR then makes more sense when you have lower interactivity and not much more logic than validation. CSR makes sense when you have high interactivity and substantial app logic beyond validation.

                                                                                                                                                                                                        But I’m a thoroughly backend guy so take everything that I say with a grain of salt.


                                                                                                                                                                                                        Edit: added a /or. Thought about making the change right after I posted the comment, but was lazy.

                                                                                                                                                                                                        1. 8

                                                                                                                                                                                                          (2) avoid splitting app logic in half/duplicating app logic.

                                                                                                                                                                                                          This is a really the core issue.

                                                                                                                                                                                                          For a small team, a SPA increases the amount of work because you have a backend with whatever models and then the frontend has to connect to that backend and redo the models in a way that makes sense to it. GraphQL is an attempt to cut down on how much work this is, but it’s always going to be some amount of work compared to just creating a context dictionary in your controller that you pass to the HTML renderer.

                                                                                                                                                                                                          However, for a team that is big enough to have separate frontend and backend teams, using a SPA decreases the amount of communication necessary between the frontend and backend teams (especially if using GraphQL), so even though there’s more work overall, it can be done at a higher throughput since there’s less stalling during cross team communication.

                                                                                                                                                                                                          There’s a problem with MPAs that they end up duplicating logic if something can be done either on the frontend or the backend (say you’ve got some element that can either be loaded upfront or dynamically, and you need templates to cover both scenarios). If the site is mostly static (a “page”) then the duplication cost might be fairly low, but if the page is mostly dynamic (an “app”), the duplication cost can be huge. The next generation of MPAs try to solve the duplication problem by using websockets to send the rendered partials over the wire as HTML, but this has the problem that you have to talk to the server to do anything, and that round trip isn’t free.

                                                                                                                                                                                                          The next generation of JS frameworks are trying to reduce the amount of duplication necessary to write code that works on either the backend or the frontend, but I’m not sure they’ve cracked the nut yet.

                                                                                                                                                                                                          1. 4

                                                                                                                                                                                                            For a small team, a SPA increases the amount of work because you have a backend with whatever models and then the frontend has to connect to that backend and redo the models in a way that makes sense to it

                                                                                                                                                                                                            Whether this is true depends on whether the web app is a client for your service or the client for your service. The big advantage of the split architecture is that it gives you a UI-agnostic web service where your web app is a single front end for that service.

                                                                                                                                                                                                            If you never anticipate needing to provide any non-web clients to your service then this abstraction has a cost but little benefit. If you are a small team with short timelines that doesn’t need other clients for the service yet then it is cost now for benefit later, where the cost may end up being larger than the cost of refactoring to add abstractions later once the design is more stable.

                                                                                                                                                                                                            1. 1

                                                                                                                                                                                                              If you have an app and a website as a small team, lol, why do you hate yourself?

                                                                                                                                                                                                              1. 4

                                                                                                                                                                                                                The second client might not be an app, it may be some other service that is consuming your API.

                                                                                                                                                                                                          2. 4

                                                                                                                                                                                                            (2) avoid splitting app logic in half/duplicating app logic.

                                                                                                                                                                                                            The other thing is to avoid duplicating application state. I’m also a thoroughly a backend guy, but I’m led to understand that the difficulty of maintaining client-side application state was what led to the huge proliferation of SPA frameworks. But maintaining server-side application state is easy, and if you’re doing a pure server-side app, you expose state to the client through hypertext (HATEOAS). What these low-JS frameworks do is let you keep that principle — that the server state is always delivered to the client as hypertext — while providing more interactivity than a traditional server-side app.

                                                                                                                                                                                                            (I agree that there are use-cases where a more thoroughly client-side implementation is needed, like games or graphics editors, or what have you.)

                                                                                                                                                                                                            1. 1

                                                                                                                                                                                                              Well, there’s a difference between controller-level validation and model-level validation. One is about not fucking up by sending invalid data, the other is about not fucking up by receiving invalid data. Both are important.

                                                                                                                                                                                                            2. 4

                                                                                                                                                                                                              Spot on.

                                                                                                                                                                                                              this turns out to be tens (sometimes hundreds!) of requests because the general API is very normalized (yes we were discussing GraphQL at this point)

                                                                                                                                                                                                              There’s nothing about REST I’ve ever heard of that says that resources have to be represented as separate, highly normalized SQL records, just as GraphQL is not uniquely qualified to stitch together multiple database records into the same JSON objects. GraphQL is great at other things like allowing clients to cherry-pick a single query that returns a lot of data, but even that requires that the resolver be optimized so that it doesn’t have to join or query tables for data that wasn’t requested.

                                                                                                                                                                                                              The conclusion, which can be summed up as, “Shell art is over,” is an overgeneralized aesthetic statement that doesn’t follow from the premises. Even if the trade-offs between design choices were weighed fully (which they weren’t), a fundamentally flawed implementation of one makes it a straw man argument.

                                                                                                                                                                                                              1. 1

                                                                                                                                                                                                                The Twitter app used to lag like hell on my old Thinkpad T450. At the very least, it’d kick my fan into overdrive.

                                                                                                                                                                                                                1. 1

                                                                                                                                                                                                                  Yay for badly written apps :-p

                                                                                                                                                                                                                  Safari will notice when a page in the background is hogging the CPU, and either throttle or pause it after a while. It puts up a modal dialog on the tab telling you and letting you resume it. Hopefully it sends an email to the developer too (ha!)

                                                                                                                                                                                                                  1. 1

                                                                                                                                                                                                                    It wound spin up on load, not in the background, because loading all that JS and initializing the page is what would cause the CPU usage. And then after I closed the page, 20 seconds later someone else would send me another Twitter link and I’d get to hear the jet engine again.

                                                                                                                                                                                                              1. 2

                                                                                                                                                                                                                I’ve never used TypeScript but am I correct in assuming that this package has translated:

                                                                                                                                                                                                                Parameter ‘onCancel’ implicitly has an ‘any’ type.

                                                                                                                                                                                                                to:

                                                                                                                                                                                                                I don’t know what type onCancel is supposed to be, so I’ve defaulted it to any.

                                                                                                                                                                                                                ?

                                                                                                                                                                                                                The TL;DR isn’t shorter, nor more clear. Probably a bad example for a prominent show case.

                                                                                                                                                                                                                1. 6

                                                                                                                                                                                                                  There’s a better (in my opinion) example on the demo website:

                                                                                                                                                                                                                  Conversion of type 'string' to type 'string[]' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.

                                                                                                                                                                                                                  is translated to

                                                                                                                                                                                                                  You can’t use ‘as’ to convert string into a string[] - they don’t share enough in common.

                                                                                                                                                                                                                  1. 4

                                                                                                                                                                                                                    You’re correct that it has translated it that way (and accurately). If you’re used to doing the work of translating type errors into what they mean in your head from working in other typed languages, it may not be more clear… but it is, in my experience, much clearer and easier for someone reading along who is learning the language. Also: brevity is not always better for clarity (some opinionated manuals of style notwithstanding), and tone can go a long way to making the language feel more approachable to folks who are new to types—which is many, perhaps most TypeScript developers when they first start, given they’re coming from JavaScript.

                                                                                                                                                                                                                    1. 4

                                                                                                                                                                                                                      Just to clarify I was not criticizing the goal of the project. I was only suggesting that perhaps another example would be more enticing given that’s the first thing a prospective user will see.

                                                                                                                                                                                                                      1. 1

                                                                                                                                                                                                                        Ah, I see – I agree that @cherryblossom’s sibling comment would indeed be a better motivating example!