1. 2

    I like Ruby on this:

    “aaaabbbcca”.chars.slice_when {|c,n| c != n }.map {|s| [s[0], s.size] }

    1. 15

      Bitcoin makes a lot more sense when you see it as performance art.

      1. 29

        I think we are (slowly) fixing such problems in newer languages. The mindset changes from “if your program crashed you’re a bad programmer and you should feel bad” to “it’s the programming language’s fault that it hasn’t caught your bug”.

        For example, look at this fork wrapper:

        fn fork() -> Result<Fork, i32>
        

        You’ll get a warning if you ignore the return value, and a compile error if you try to use as a pid without handling the error somehow. Even if you want to handle the error by aborting the program, you still have to write that explicitly. And the Fork type is an enum Parent | Child(pid), so you have to match on it, and you can’t just run with let pid = fork().

        1. 6

          While I completely agree newer languages help here (specifically those with algebraic datatypes), it isn’t an end-all solution. In Rust, I’ve seen many people just bubble up errors like this with ? and it ends up acting like an exception (especially with libraries that automatically convert all errors into a universal type). This is 100x better than just failing with segfault, but it still doesn’t actually handle the problem.

          It really is just a cultural issue. So many developers believe solving the problem is just handling the happy path, when true engineering is when you handle both the happy path and the failure path. There is only so much a language can actually do here. If a developer believes “good” code looks only like the clean happy-path code then they are doomed to write terrible code.

          1. 12

            still doesn’t actually handle the problem

            Depending on the kind of program, just aborting is a perfectly valid way to handle an error, especially a “fork failed” kind of error — essentially the “your OS environment is kinda screwed up” condition.

            You definitely don’t want that in your spacecraft navigation controller (but that ideally shouldn’t have any dynamically-allocated resources at all), you would prefer to avoid it in a document editor with unsaved state (although users of those are conditioned to Ctrl-S early, Ctrl-S often), but who cares if a media player or system info monitor just gives up when your system is out of resources.

            The key problem with the old school C error handling is that a quick segfault in the right place is the good outcome. lldb -c program.core program, bt, “oh I see lol”. The far more insidious issue is that the program can go on as if nothing bad happened and start doing wildly incorrect things, and the article’s example of sending a signal to the -1 “pid” returned by the failed fork call is a perfect example.

            1. 2

              True, arguably for most applications simply failing with a relevant error message is appropriate. I think taking the article’s argument in general, and not specific to fork or segfaulting, properly handling errors is not something that is taught or even well regarded.

              1. 2

                This is the crux of the problem, IMO. That different applications need to handle errors differently AND that different kinds of errors within the same application need to be handled differently.

                That’s why, IMO, a general purpose language needs at least three failure mechanisms: one for “you used the API incorrectly or fed invalid data”, one for “something unexpected happened- let’s message the user and see what they want to do”, and one for “holy shit! There’s no way this will work any more. Just quit”.

                Rust is pretty damn close to right, IMO. My only complaint is that Rust (the std and some of the ecosystem) is still too eager to publish panicking APIs without at least offering a non-panicking version. In general, I rather have the non-panicking version than the panicking version because I can choose to panic on receiving the failure response. It’s harder to “unpanic” if you even realize that a panic might happen.

                Swift is kinda-sorta there. It has throws and it has Result. But it seems that nobody actually uses Result as a return value.

                Even in Java you were “supposed” to use checked exceptions for expected failure modes and unchecked for the cases where you need to just bubble all the way up or just crash.

                I’m super disappointed that Kotlin, for as ergonomic and awesome as the language is, reverted to just using unchecked exceptions for everything. That’s such a crappy way to do error handling/signalling.

            2. 3

              I want to add to your point. The kernel API matters too; the Linux fork syscall still takes a pid_t, which is a numeric type that indicates something like Parent | Child(u32 pid) | Error(u32 errno) when combined with C’s errno global error information. Programming language runtimes can only paper over the issue, as when this particular functionality is shaped like CreateProcess and doesn’t allow the fork/exec split.

              The very nature of fork is that parent processes are invited to confuse themselves as to who they are and with which privileges they are running. All a programming language can do in this situation is delay implementing the raw syscall at user level, and try to write enough tamed wrappers to keep up with demand.

              1. 4

                And this in turn is probably has a lot to do with the fact that the linux kernel is written in C (and thus suffers from C’s primitive type system), and POSIX dates back to a time when C and languages with similar type semantics to C were the only game in town when it came to writing an OS. If I was writing an OS from scatch today, I would do so in a language that did let me define the return type of the fork syscall with a sensible algebraic type. But that’s not C.

              2. 2

                It’s worth looking at how Erlang handles it too.

                1. 2

                  In case you’re referring to fork specifically: I don’t think there’s any way to reasonably use fork in the Erlang VM. Generally multi-threaded VM runtimes don’t give you any API for fork, and if you just use FFI to call it from your program, prepare for unforeseen consequences.

                  If this is about error handling in general: both exceptions and pattern matching, but of course dynamic pattern matching since it’s dynamically typed. i.e. there’s no Either type, just tuple conventions like {ok, <your result>} vs {error, <your error>} (where ok and error are atoms). And of course you can use try to turn exceptions into tuples to match, and match and throw to do the opposite.

                  1. 2

                    I wasn’t going specific on fork but, rather, what it enables. In Erlang you spawn processes. As far as I can tell, you are guaranteed to get a valid pid from spawn and there is a good story about what happens you use a pid for a destroyed process. It’s a case where a differently designed mechanism makes a class of errors impossible.

              1. 21

                This could be one of those hard cases that I talked about recently. This is mostly critiquing his programming, but then there’s notes about his business work that he’s now more famous for, and the business stuff is off-topic here. I’m not removing this because it’s mostly programming. Please help maintain the topicality of the site by not diving into his business and politics. (And reminder: anyone is welcome to help work through the above cases to figure out where to draw the line and how to express it. Those comments I just linked are my current thinking as I slowly work towards getting more of this more explicitly into /about.)

                1. 30

                  I enjoy bashing PG and startupcanistan as much as anyone, but this critique was heavy on “PG is an ossified hasbeen reactionary” and light on good critiques of Arc.

                  An article about why Arc has deficiencies and what we can learn from it is one thing; character attacks in the guise of technical critique are another.

                  I am as sure the real damages and harm PG has done are nontechnical as I am sure this is offtopic.

                  1. 24

                    More succinctly: we wouldn’t celebrate an article attacking Larry Wall or Richatd Stallman instead of Perl or Emacs.

                    0r at least, I would hope we wouldn’t.

                    1. 10

                      I don’t have as much faith in the ability of the lobsters commentariat (and moderation team) to fairly judge what content is too political to be on-topic as you do. I would say that merely using the word “reactionary” in a pejorative way makes this article far more political than, say, anything I’ve ever posted here about Urbit that was flagged as off topic or trolling.

                      1. 3

                        Just to note that the bar for discussing an article here shouldn’t be that it’s worthy of celebration. What’s being discussed is whether this is on-topic at all.

                        1. 9

                          Consider the case of an article about, I don’t know, old IBM punchcards. Perfectly good information. Additionally, the author goes into Holocaust ramblings. How much other stuff are you willing to put up with?

                          The exploit being used in this article is “mix nontechnical political content, e.g. character assasination, in with sufficient technical content, e.g. language design”.

                          The article itself could’ve been written purely as a critique of Arc, with a passing reference to its designer, but that clearly isn’t why it was written.

                          1. 8

                            This isn’t even close to character assassination. It gives due praise but delves into a serious critique of character or maybe more accurately of method and intent. That was the point of the article. The technical content isn’t an excuse for the political content, it’s an illustrative example. The fact that the article isn’t a good fit for Lobsters shouldn’t matter to the author one bit.

                            1. 9

                              It gives due praise but delves into a serious critique of character or maybe more accurately of method and intent. That was the point of the article. The technical content isn’t an excuse for the political content, it’s an illustrative example.

                              Thank you for making my point!

                              Lobsters isn’t a site for character critiques and other drama gussied up with supporting technical details.

                              1. 6

                                Well, in theory a lot of people take technical advice from his essays on programming languages, language design, etc. If someone believes that’s a bad idea, it is 100% fair game and technical content to make that argument. Not long ago there was a piece that critiqued taking technical advice from Bob Martin by pointing out problems with Clean Code, for example.

                        2. 1

                          More succinctly: (make-my-point)

                        3. 6

                          I disagree, it is fairly technical and on point with Arc.

                          Speaking as someone who actually wrote a program in Arc when it was released.

                        4. 24

                          Is this a reasonable summary of the article?

                          • PG’s writing has taken a reactionary turn
                          • Brevity in language design is a flawed and unrigorous notion. He’s using his intuition, which has not held up to reality
                          • This is evidence that he uses his intuition everywhere; his opinions about politics shouldn’t be taken seriously.

                          It’s a fair enough set of observations, although I’m not sure the argument is air tight. It’s also a very roundabout way of refuting political arguments… I’d rather just read a direct refutation of the politics (on a different site)

                          1. 16

                            Yes, the politics mentioned in the introduction felt out of place. The rest of the article was well-written and dispassionately argued, but I couldn’t help feeling the whole piece was motivated by political disagreements with Graham (epitomized by the coinbase tweet), and that diminished its impact for me.

                            1. 8

                              I don’t think it’s as clear as it could be, but I read the article as starting from the assumption that Graham’s recent political and social writing is poor, and then asking whether the earlier more technical writing is similarly flawed.

                              If the argument went the way you said, it would be pretty bad. This is why I think talking about logical fallacies is less valuable than many people think. It’s usually pretty easy to tell if a precisely stated argument is fallacious. What’s harder is reconstructing arguments in the wild and making them precise.

                              1. 13

                                Yeah, if you want PG criticism, just go straight for Dabblers and Blowhards. It’s funny and honest about what it’s doing.

                                https://idlewords.com/2005/04/dabblers_and_blowhards.htm

                                This article spends a lot of words saying something that could be said a lot more directly. I’m not really a fan of the faux somber/thoughtful tones.

                                (FWIW I think PG’s latest articles have huge holes, with an effect that’s possibly indistinguishable of that of willfully creating confusion. But it’s also good to apply the principle of charity, and avoid personal attacks.)

                              2. 5

                                You’ve removed an implication that libraries matter as much as the base language, some negative remarks on Paul Graham’s work as a language designer, and some positive remarks on Paul Graham’s overall effectiveness as a developer, technical writer, and marketer.

                                But yes, the article seems fairly well summarized by its “This is all to say that Paul Graham is an effective marketer and practitioner, but a profoundly unserious public intellectual (…)”.

                              3. 12

                                I’m not removing this because it’s mostly programming.

                                The programming that is mentioned is there to make a case against a person and extend it to a broader point about people. I would’ve made the call the other way.

                                1. 17

                                  There’s a lot of interesting insight here into how to do language design (and how not to do it). I’m glad it stayed up.

                                2. 8

                                  This is is a tricky one, yeah. It feels like there’s really three things going on in this article:

                                  • The writer is bashing Paul Graham
                                  • The writer is making it about Paul Graham’s political/social writings
                                  • The writer is supporting those by talking about Paul Graham’s history in the technology field

                                  When looked at that way, I’d lean slightly towards it being offtopic. If one wanted to write something about the design of Arc and the history and origin of design mistakes and the personality of the person that resulted in those mistakes, one could re-use the same arguments in this article and do so. I think one would come up with a very different article if so. So it’s not about the tech or the intersection of humanity and their artifacts, it’s about Paul Graham and their opinions.

                                  1. 6

                                    I’m glad this article about PG made it to lobsters, otherwise I wouldn’t have seen it. I’ve had a similar journey with PG’s writings as the author, going so far as to purchase Hackers and Painters when I was younger and thought programming made me special. I enjoyed learning a bit more about arc than I would have had this article been moderated off the site.

                                    1. 5

                                      Thanks for your hard work @pushcx! Moderation of topics is what makes lobsters great!

                                      1. 4

                                        My impression on this meta-topic of to what extent lobste.rs should have posts that touch to some extent on non-technical questions: We’ve arrived at a point where there’s a group of readers who will indiscriminately flag anything off-topic that falls into this category, and another group of readers who will indiscriminately upvote anything of this category that makes it through.

                                        I’d suggest that it might be better for the health of the community to be a bit more permissive in terms of what political/social/cultural tech-adjacent stories are allowed, and to rather aim to let those that don’t want to see those posts here filter them using the tagging system. (But I’m sure that’s been suggested before.)

                                      1. 1

                                        The best thing is to design away error handling.

                                        1. 4

                                          There are many language features that are both documentation and enforcement. Private is like that. Anyone can come by later and change it to something more visible. There’s some wiggle room there.

                                          A friend once made an IDE plugin he called Thatcher which went through a Java project and made everything private that could be private. To me, it seemed like a great idea as long as people felt free to increase the visibility as their design changed.

                                          1. 2

                                            That does sound like a good idea. It’s like dead code elimination, but for interfaces. Nice name, too.

                                          1. 15

                                            This is (related to) one of the most frustrating parts of working with Kotlin for me: no package visibility.

                                            Kotlin chose to abandon Java’s package visibility modifiers, arguing that it didn’t really “protect” your code because anybody else could trivially write code under the same package name and then see all of your package-private code.

                                            But that’s missing the point. We don’t usually write “private” because we’re afraid of people seeing or using the code. We write it so that they don’t have to see the code. Having an interface that is as small as possible reduces the cognitive load of someone consuming your library/package/class.

                                            I feel like the cognitive load aspect is something not discussed as much.

                                            Aside: In Kotlin, the suggestion is to just use “modules” instead of packages if you want that kind of protection, since it offers “true” protection from consumers accessing the private parts of your sub-code. I hate that because it’s more effort to move pieces around between modules, to change the public/private interface of a module, etc.

                                            1. 4

                                              Historically, languages have been poor at dealing with levels of abstraction above the class. When they do deal with them, they often aren’t first class constructs. It’s a shame. One could argue that micro-services (and many other things like OSGi) came from the absence of these abstractions in languages. My sense is that language designers don’t want to commit to a deployment model. Sadly, protection loses too.

                                              1. 3

                                                Yes. It is a shame. I feel like Rust modules are pretty nice, but then, I also don’t mind Java’s packages at all, either.

                                                What I find uncomfortable is this recent trend of the language acknowledging the concept of a file when it comes to privacy/visibility (e.g., Swift and Kotlin). That just feels weird and wrong to me…

                                              2. 2

                                                Exactly! Every namespace is a precious resource to be maintained as neatly as possible.

                                                1. 2

                                                  We write it so that they don’t have to see the code.

                                                  I didn’t mention it in the article, but the “scissor test” is a concept that I like in regard to this. The idea is that if you were to print out a file of code on to paper, there should be a line that you could cut through with a pair of scissors that separates the interface from the implementation details. So if you want to use the class, you only need to read up until the scissor line, but if you want to understand how it works under the hood you can continue reading further. The scissor line is basically where private starts.

                                                  1. 2

                                                    Kotlin chose to abandon Java’s package visibility modifiers, arguing that it didn’t really “protect” your code because anybody else could trivially write code under the same package name and then see all of your package-private code.

                                                    Interesting. My tendency would be to go the other way and eliminate private and protected, but keep package. Anything in the same package as a class that depends on implementation details of that class is easy to refactor at the same time as a change to my implementation, so I don’t gain anything much from private and protected that I don’t have from package.

                                                    If you add a new class in my package, then you are effectively forking my package. That’s fine, it’s up to you to decide that the maintenance burden of doing so is worth it for the change that you want to make. If I refactor my package and break something that your class depends on, that’s your problem because you have a downstream fork of my package, not just something using the package.

                                                    1. 1

                                                      Kotlin, as you mentioned, has internal visibility to hide things between modules. I find it nicer than package-private since you don’t need to have one package with many classes inside (subpackages’ classes can’t access the package-private members of a class in a parent package)

                                                      1. 2

                                                        I forgot that Java has no concept of subpackages, which is also disappointing. Rust modules seem to be the winner, then, from my experience.

                                                    1. 2

                                                      This is really neat. I actually introduced a junior engineer to the idea of characterization tests a week ago, and wish I had this to serve as an example. Will show it to him anyway, but also show him how simple the code behind it all is.

                                                      1. 1

                                                        Thanks. Great to see the idea spread.

                                                      1. 5

                                                        I see another way that they are the same. When I talk about inheritance, I mention that a good way of thinking about it is that abstract classes are classes with holes in them and subclasses fill the holes. Don’t inherit unless you are filling holes, i.e, overriding abstract methods.

                                                        HOFs are like those overrides and the places that accept them are the holes.

                                                        It’s parameterization of behavior and, like the article says, if it’s done excessively it can be confusing.

                                                        1. 7

                                                          A rather silent truth about the industry is that there are many teams that are self-organizing in the way that the OP reports and many that have leads. If you visit teams in both camps, each will swear that their way is the right way but, in my experience, both work. This is very much like the dynamic vs. static typing debate.

                                                          A big reason for this bifurcation is culture. If decision-makers in management have Agile lineage, the flat, leaderless team is more prevalent. Trad organizations tend to have leads. Especially, in my experience, teams that use MS languages. It really seems to be a culture thing. Teams and orgs tend to do what other teams and orgs within their line of sight do.

                                                          1. 4

                                                            Looks very interesting. I’d be interested in seeing reviews.

                                                            1. 11

                                                              I bought it and have done a read through. I’m interested in algebraic structures as building blocks for programming, verification through specifications, and denotative design, and have spent time with all these concepts. I’m probably on the advanced side of the target audience.

                                                              The book covers the process of creating “laws” to model a problem, using a mechanical process to create a naive implementation, using tooling to derive quickcheck specs, and then creating a better implementation using those specs as verification you’re not breaking your domain model. The modeling is similar to denotative design though not quite as formal on its notation. It does show that domain problems can be modeled with Monoids, Functors, Functions, etc and includes an appendix to discuss these structures. It uses haskell as it’s code and tooling environment, though I can take the concepts and use them in clojure.

                                                              The sample of the book covers the first problem, which I recognized from https://github.com/conal/talk-2014-lambdajam-denotational-design and Conal Elliot’s other work. The second domain problem deals with matching user photos to a location to grant rewards. Think about a scavenger hunt or pokemon go style game. I still need to reread and spend some more time processing this example to really get everything deeply.

                                                              I’d have enjoyed another example of a problem domain going through the design phases. Having the first one be from prior sources is fine for introductions to the ideas, but I was hoping to see it applied multiple times to something new.

                                                              Seeing the use of tooling over the naive implementation to generate quickcheck specs, even ones not in the original design’s laws, was new to me and cool to see. Also the second example eventually gets complex enough that I think it shows the value of the specs.

                                                              Overall, I’m happy with my purchase of the book.

                                                              1. 5

                                                                There’s a pretty interesting book called “Algebraic Models for Accounting Systems” that I stumbled onto. You might be curious:

                                                                https://www.amazon.com/Algebraic-Accounting-Systems-Salvador-Rambaud/dp/9814287113

                                                              2. 2

                                                                There’s a link to the first few chapters at the bottom of the page.

                                                              1. 1

                                                                I’d love to see a frequency histogram of percentage of a book read for books that have been purchased and started. I imagine it to be quick drop off to a long valley that rises to a small peak at the end.

                                                                1. 17

                                                                  Requires a sign-in to read. Unread.

                                                                  1. 8

                                                                    Is there a way to filter content by domain? I never want to give medium.com my eyeballs

                                                                    1. 2

                                                                      Fair enough, and I agree.

                                                                      But if you like you can work around the problem by telling your browser not to accept cookies from medium.

                                                                      1. 2

                                                                        I opened this page both in Firefox for macOS and Firefox for Android, and both browsers showed me the full article without me having to sign in. I was able to view the article despite not even having a Medium account.

                                                                        Maybe your problem is related to this message I see at the top of the page:

                                                                        You have 2 free stories left this month. Sign up and get an extra one for free.

                                                                        Try opening the page in a Private Browsing window?

                                                                      1. 4

                                                                        It’s interesting that the type information is to be kept in a kind of ‘signature’ file, separate from .rb code files. I wonder why they’re not allowing it alongside existing code. Perhaps it can’t be done in a backwards-compatible way - or a non-messy way?

                                                                        Elixir allows adding ‘typespecs’ above function definitions like this:

                                                                        @spec path_clear_between?(intvec2d, intvec2d, MapSet.t(intvec2d), number) :: boolean
                                                                        def path_clear_between?(a, b, others, epsilon),
                                                                          do: # Implementation goes here
                                                                        

                                                                        Having the types specified in this way does work, but it’s a bit of a trade-off. Elixir code often looks like this:

                                                                        def to_360(a) when a < 0, do: 360 + a
                                                                        def to_360(a), do: a
                                                                        

                                                                        Or this:

                                                                        defp combinations(_, 0), do: [[]]
                                                                        defp combinations([], _), do: []
                                                                        

                                                                        … where the choice of which function is called happens at runtime based on guards (first example) and/or pattern matching the arguments.

                                                                        Ruby doesn’t have a mechanism like this (as far as I’m aware) or overloading (dynamic despatch based on type) so it would make more sense for arguments to have their types specified ‘inline’ - as shown in the example .rbs file.

                                                                        Perhaps if the .rbs idea works, the next step could be to make the Ruby interpreter(s) allow an inline syntax?

                                                                        1. 2

                                                                          I pitched the idea of a separate file a long time ago. ( https://www.artima.com/forums/flat.jsp?forum=106&thread=155960 ).

                                                                          The idea was to maximize flexibility and decouple the code from the typing. They must have been thinking along those lines given their support for interface types and what they call non-uniformity.

                                                                          The nice thing is that what they have described looks forward-compatible with adding the type specs to the Ruby source eventually. On that day, there will likely be a tool to collapse .rbs types into class, field, and method definitions in .rb files.

                                                                          1. 4

                                                                            That’s also what we do in CHICKEN; there’s inline syntax for types which gets used when compiling just that module, but the types then are exported into a “types database” file which gets consulted when compiling other code which uses the module.

                                                                            It is also possible to supply a manually-written types file, which gets used when compiling other code against the module it corresponds to.

                                                                        1. 28

                                                                          “Considering the factors mentioned above, we can reason that unit tests are only useful to verify pure business logic inside of a given function. Their scope does not extend to testing side-effects or other integrations because that belongs to the domain of integration testing.”

                                                                          Here’s the trick. It’s painful to unit test non-pure business logic or elaborate integrations, so pain in testing gives you a hint that your design could be better if you manage to avoid those. It’s worth checking to see if you can. In the example, the author has already settled on a design and discovered that it is hard to test, but the additional work of thinking about alternative designs that are more easily testable and have better cohesion and less coupling doesn’t appear to have been. Sometimes it works, sometimes it doesn’t, but it’s great that testability gives us feedback about design.

                                                                          In this case, I’d look into whether this can be done with tell rather than ask semantics. With objects, ask calls tend to increase coupling. ( https://martinfowler.com/bliki/TellDontAsk.html )

                                                                          1. 1

                                                                            There are a few more things to consider about wide contracts: user expectation & risk.

                                                                            My favorite example comes from languages which allow negative indices to be used to index backward from the end of a string or a collection. It seems unnatural or counter-intuitive until you get used to it, but with acclimation it’s just considered normal. It helps that the risk profile isn’t very bad either. The chances of indices into collections rolling over into negative on overflow have decreased as larger integers have become common.

                                                                            In general, I favor widening contracts when we can and I push rather hard to see if it is possible. The ideal, for me, is to validate at the highest level and then use more generic code below that can handle cases wider than should ever be needed.

                                                                            1. 8

                                                                              Stopping all other development to make a code base consistent implies telling all developers that are not involved in the change to wait. The larger the team, the more expensive this is.

                                                                              This would imply that working on software without irreversibly accumulating tech dept is impossible with a large team. Maybe that is actually a pretty fundamental truth.

                                                                                1. 2

                                                                                  Thanks for that link, I see a lot of value in this idea!

                                                                                  However, this assumes a team of max 5 to 7 people (10 assistants/facilitators for a single lead engineer seems overkill) and single simultaneous focus.

                                                                                  I currently work in a company with about a 100 engineers working on a single piece of software. Although there are extremely high code quality standards, all the latest CI practices and rigurous review processes, the issue described in the original article is clearly present, the code base is in constant flux and various global concepts are being applied simultaneously.

                                                                                  And I don’t see any other possible approach with a team this size.

                                                                                  1. 4

                                                                                    Since scaling pressure of team size impacts systems, we should probably architect to reduce the size of the teams we need. More modularization.

                                                                                    1. 1

                                                                                      That would be ideal, however you probably want to see a level of conformance to the same code conventions across all modules or even across all products the company provides, for example in order to make it easier for new hires to work on several parts of the product or on several related products at once. And that takes you back to where you started: multiple simultaneous gradual and never quite complete implementation of global refactorings.

                                                                                      On the other hand, perhaps conformance to conventions across modules and products to facilitate flexible employability is an ambition you could choose to forfeit in order to have a way to prevent tech debt from accumulating. I wonder if there is a way to measure some kind of optimum here.

                                                                              1. 17

                                                                                First of all, this conventional wisdom assumes that there is a single universal definition of “better”; but there isn’t one. If you work on a project as a team–which is almost all of us–it’s really hard to get everyone to agree on something; be it the definition of clean code, or even the type of whitespace to be used for indentation.

                                                                                Having a really small team size (N) makes consensus easier. It amazes me how much process is compensation for having more than a couple of people (at most) work on something. The failure mode for a small team is bus factor. The failure mode for larger teams is silent accumulation of technical debt due to lack of consensus and coordination.

                                                                                I don’t think there’s a neat solution to this problem, but I do notice that many problems go away as N approaches 1.

                                                                                Two might be the sweet spot.

                                                                                1. 3

                                                                                  Which is why high productivity is so incredibly important. Get the highest productivity people (see Brooks’s chief programmer teams) and do everything to help them be more productive.

                                                                                  1. 2

                                                                                    The failure mode for a small team is bus factor. The failure mode for larger teams is silent accumulation of technical debt due to lack of consensus and coordination.

                                                                                    I’m not sure that this concept has ever been made as clear as this quote. While not particularly a new observation, the profundity of this quote is one of those that I feel will grow to be just as important over time as Knuth’s famous quote on optimization.

                                                                                  1. 11

                                                                                    Airline miles and hotel points, right?

                                                                                    1. 7

                                                                                      But with blockchain!

                                                                                    1. 17

                                                                                      The following comment on the orange site deserves to be amplified. It’s something I’ve been saying for a while, but not this eloquently.

                                                                                      Since communication overhead increases proportionally the square of the number of people on the team—a fact illuminated by Brooks in the 1970s—what you actually want is as little collaboration as you can get away with. It’s an important point that warrants repeating. It comes from an observation that if there are N people communicating directly with each other, every one has to communicate with N-1 other people, which leads to N*(N-1)/2 simultaneously open bidirectional communication channels, which is O(N^2). I think this point plays a crucial role in why hierarchies form, both in teams/companies and societies. A hierarchy is what lets you turn an O(N^2) relationship into O(N) one, at the expense of creating O(log n) hops.

                                                                                      1. 4

                                                                                        Coda Hale covered this pretty eloquently in the post “Work is Work” last month:

                                                                                        https://codahale.com//work-is-work/

                                                                                        With the punchy takeaway: “Keep the work parallel, the groups small, and the resources local.”