1. 63
  1.  

  2. 17

    I heard a similar story with a voice service company in Montreal that had a Lisp codebase. They had high productivity with a small team, but when they had to grow they decided to switch to Java, and that decision was not made only by management, but by the system’s authors as well. The reason one of the lead developer gave me was that it was hard to hire and train and that the use of macros made it too specific and then hard to get into. This article echoes these points almost exactly. At the studio we’re using inhouse languages, and it also only works because we’re a small, stable team.

    1. 21

      As the size of a team and codebase grows, the ratio of time spent writing code to reading other people’s code drops. Languages that make it easy to be productive while writing code don’t necessarily make it easy to read other people’s code. If you want to have a large team working on a large codebase then you want a language optimized for reading not writing.

      1. 4

        So what are those? Java doesn’t seem to be ‘optimised for reading’ to me.

        1. 2

          It’s not. Go seems to be pretty easy to read, although perhaps there are other languages even more readable. Both Lua & Elixir strike me as contenders based on their low keyword count, although I haven’t actually used them so I can’t vouch for that based on experience. See https://medium.com/@richardeng/how-to-measure-programming-language-complexity-afe4f7e75786

          1. 4

            This is probably Go’s greatest strength. If you can get used to the Haskel-like syntax, Elm is quite possibly the most readable language currently in existence, though it sacrifices a lot to get there. And it’s no accident that these two languages are so readable. Go and Elm were both designed to be easy to get started with.

            I personally find Rust to be very readable compared to C, C++, Java, Python, Ruby, and any language that promotes traditional OOP practices (i.e. inheritance hierarchies), primarily thanks to its abstract data types (ADTs) (i.e. Rust enums), match expressions, and trait system, but also because the borrow checker strongly encourages ownership and control-flow resembling an directed acyclic graph rather than the tangled mess you often end up with in OOP-heavy languages.

            1. 2

              Don’t you mean algebraic data types? I always thought that was what Rust enums resemble.

              1. 1

                Yes, thanks for the correction. I often confuse the two names.

          2. 1

            I would imagine it’s directly tied to the amount of ambient magic in the system. Things that provide functionality for X, but are left out of the code for X “because boilerplate”.

        2. 8

          “Stable” is a key and often ignored point. In today’s world, people don’t stay in companies for longer than three of four years. This is specially true when engineers hit their late 20s and can command “senior” titles and salaries by switching jobs. That means that the language and stack you choose to use should make both hiring and onboarding as quick and easy as possible.

        3. 17

          I suspect most of us here look at this from the perspective of professional programmers who are building products for someone else to use. But remember, that’s not what Smalltalk was invented for. It’s the language of the DynaBook, which is supposed to be a personal computer that allows everyone to enhance their life and work. So programs aren’t intended to be shared or maintained in teams — they’re supposed to be part of a long-term symbiotic relationship between you and your computer that makes you happier and more productive.

          Unfortunately we’ve instead built a world where mostly it’s only specialists that can program computers effectively, so this sort of environment seems dangerous and impractical.

          1. 2

            It’s particularly difficult in a networked world; Smalltalk systems weren’t built with any meaningful protection mechanisms as far as I know, meaning the changes you make can open up a whole new world of problems.

            1. 2

              That’s true of Smalltalk-80 proper, but there are a number of small tweaks you can do to fix that particular issue. NewtonScript locked the core prototypes, for example (which had positive RAM implications, too), and there have been some prototype Smalltalks where class changes, or even additions, are invisible except for classes within a certain package—not too dissimilar from Windows side-by-side installations, and allows for existing Smalltalk code to modify the world from its perspective without mucking with core classes.

              I think if this had truly been the issue that needed solving, it’d have been. But as someone who coded Smalltalk on and off from maybe 1995 until about 2011, I’m with the others in this thread who are highlighting more secondary problems,

              1. 1

                I didn’t mean for it to come across as the issue :) My intent was to point out another aspect of the original goal of Smalltalk that would need some thinking about today.

          2. 11

            The fact you can’t hire 500 Lisp hackers is a feature, not a bug; why do you need 500 devs, anyway?

            It does get to larger questions of institutions: why do they exist? To further propagate themselves? Or to solve the problem they purport to solve?

            1. 8

              why do they exist? To further propagate themselves?

              To enrich their owners, present and future, while providing lots of wealth and power to their executives along the way. Plain and simple for vast majority of midsized to big companies.

              1. 6

                That’s why a lot of small institutions exist too. :)

                1. 1

                  Haha. He said “hire 500 Lisp hackers.” I thought that it was medium to large organization was implied context. Probably still should’ve worded it differently.

            2. 5

              My theory, completely made up and untested, is that these powerful Lisp/Smalltalk systems weave code and brains together in a way that amplifies both.

              I think that is a universal property of programming. Cf. code as design, programming as theory building and others arguing that programming and design are interweaved such that the code supposes a context and background knowledge to make sense and tell a story the developers can understand. New developers need to become familiar with the context, background and story to understand the code and make changes consistent with them.

              1. 4

                Yes. Back when Rails was making a splash, the amount of noise generated over the fact it uses convention over configuration was astounding: the time spent on boarding a new hire is not bound by the fact your web app handlers don’t follow a strict file name convention, but rather the domain itself, the history of how it has evolved, and the specific trade offs made.

                As usual, these matters are less fun to talk about, so we fixate on the tech instead.

                1. 3

                  In light of this, I wonder if it’s a mistake to work on a project for a significant length of time as a solo developer, unless one intends to be solely responsible for it forever. Maybe a team project has to start out as a team project, so the domain-specific knowledge isn’t all in one person’s head. What do you think?

                  1. 3

                    Modeling the domain as explicitly as possible (eg DDD) and keeping it separate from gunk like frameworks and the web goes a long way towards making a system that is easy to pass on.

                    1. 2

                      It appears to me that DDD has morphed into being all about the “gunk like frameworks” rather than the problem domain. To so many today it requires Event Sourcing perhaps with CQRS etc. The idea of using language constructs to model domain concepts, their interactions and the constraints between them as exposed by Eric Evans book (i.e. an object model), is lost in the rush to how to model plumbing like Aggregates and Bounded Contexts.

                      My belief is that it has become this way because of the huge numbers of Microsoft background developers who have grown up with Microsoft’s data modeling approach, where there is no rich object domain model but instead a transactional service layer that interacts with a domain layer that just abstracts a database. Eric Evans book concepts dont really fit that anemic domain model approach.

                      1. 2

                        Fully agree. The ideal of Evans hasn’t really been realized in mainstream understanding. Which is kind of funny, because it is conceptually much simpler than stacking myriad layers of gunk atop one another.

                    2. 1

                      I think the knowledge can be transferred, but it takes time. If you start working together with the one holding the knowledge and communicate about ongoing development, discussing requirements, designs, code, …, taking care to go into the what/how/why/when/where/who/whence/… and the history of each of those, then eventually all knowledge will be transferred.

                      To do that you need to have someone willing to think of and ask a lot of questions and someone willing to answer them and expound freely (as well as an environment that allows that). There are all kinds of possible obstacles that prevent this from working. If asking questions makes someone feel dumb, being asked questions makes someone feel second-guessed, history doesn’t seem important, management doesn’t allow prolonged chats, etc.

                      I think the knowledge transfer can be sped up significantly if you manage to first convey the high level structure of that knowledge. That should follow from answers to high level questions. Why does this company/project/product/service exist? How do the people involved view its future (and how has that changed ober time)? Who are the (intended) customers/users? What development philosophy, traditions, convictions, … has guided development? What have been critical decisions?

                      An advantage of starting in a team, even if only of two, is that you are already forced to make a lot of things explicit to explain them to the other, with the opportunity to write them down somewhere. You can choose to document every hack, every design decision, every shelved idea, every known shortcoming, etc. Also the answers to the higher leveI questions mentioned in the previous paragraph. I think a couple of documents can go a long way towards providing the framework for all the knowledge.

                      Anyway, this is just a scattering of thoughts on the subject and quite non-exchaustive; I wonder if there’s a good source that addresses these issues…

                2. 5

                  I’ve experienced the same effect working with Erlang, Haskell, and now Rust, where the people who were there were often there because of passion instead of being thrown into that bucket by management or because that is the language that is likely to be chosen due to industrial trends. Being able to acquire professional-grade skills in niche languages often has the false appearance of being a luxury that is unlikely to pay off for your career.

                  None of these languages are hard to learn, given enough freedom and incentives in your life to pursue them. But most people are thinking about keeping the bills paid and staying employable, and niche languages can feel difficult to justify the time investment in.

                  Personally, I’ve found my investments in these languages to let me work on teams full of passionate and productive engineers. It’s easy to become a recognized voice in a small community, and the job offers become much more interesting. And by being one of the few companies working in that niche language in a city, I’ve been able to hire excellent talent for low effort.

                  The same was true for pre-1.0 Go, but it quickly graduated due to becoming quite easy to gain professional-grade skills in, and it’s clear that if you want to build services that you’ll be able to find companies who want to pay you to do so if you know a bit of Go. Now you can get the 10k foot view of how things work in a weekend, which is wonderful for opening up that community, but I think this effect has been lost in that community due to proficiency having fewer preconditions.

                  1. 4

                    Yeah one thing I’ve learned over my career is that there’s a tension between what’s good/productive for “you” and what’s good for the team.

                    It’s just like in life – there’s no way around it. As an analogy, I think it’s true that Americans value individualism more but other cultures value the group more. Neither is right or wrong, but it’s a different value.

                    I think the key is to recognize this and change your behavior depending on the situation. If you’re working with 100 developers on Clang, follow Clang’s style and choices. If you’re starting your own side project, then go nuts with your own language!

                    Also, I think Paul Graham and other “powerful language” advocates would agree 100%… they always advocate small teams as well. I don’t think it’s that controversial!

                    1. 2

                      There are also differences between what is good (in the longer term) and what is easy (in the shorter term) that factor in.

                    2. 3

                      I think some of it is becaue image-based development does not have good collaborative tools

                      1. 5

                        That’s always been a bit dubious (Smalltak has had changesets since at least the late 80s), but it’s been truly false for a long time. Squeak had Monticello, VisualWorks had ENVY and StORE, and Pharo just uses Git straight-up these days. I’m not arguing images don’t have other issues with them, but collaboration isn’t one of them.

                        1. 2

                          completely fair point. I didn’t only mean source code control, I’m also thinking that the developer process, incrementally manipulating a running image isn’t very easily mapped onto distributed working, maybe never was?

                          e.g. are there workflows/tools where multiple developers push changes to a central image ? Because that’s kind of the mapping there - if I’m writing C, I am diffing text files, and compiling the changed ones into new objects, linking everything, running tests - this extends quite naturally to continous integration, and automation for collaborators.

                          When I’m working on an image style system, I’m updating a running thing typically, usually interactively testing as I go. Ideall collaboration flow for this kind of thing would be to pull small upstream changes directly into my image, switch branches without resetting the world, this kind of thing.

                          I don’t know very much about the detail of your counter-examples, but I did not mean to suggest it was impossible, so much as ungainly, which was my understanding.

                          1. 3

                            Sorry for responding so late; I know others won’t see this, but thought you deserved a response.

                            I’m also thinking that the developer process, incrementally manipulating a running image isn’t very easily mapped onto distributed working, maybe never was?

                            You do kind of have to decide if you’re gonna work in the classic Smalltalk mold, or if you’re going to work in a modern mold; that’s fair. It’s just that the modern mold is really common, to the point that relatively few people sculpt an app out of a Smalltalk image (which is closer to the original intent) than write Smalltalk code that really is the program.

                            are there workflows/tools where multiple developers push changes to a central image ?

                            This is in fact exactly how at least GNU Smalltalk and Pharo (which is to Smalltalk what Racket is to Scheme) do. E.g., this is Pharo’s Jenkins server, which works by just building off master constantly, just as any other project would do. The only difference is that, rather than diffing or thinking in terms of files, you think in terms of changes to classes and methods. Behind the scenes, this is converted into files using a tool called Iceberg.

                            The only place this system calls down is if you’re building constants directly in the image, rather than in code. E.g., if I were truly building a Smalltalk program in a traditional Smalltalk way, I might just read an image into a variable and then keep the variable around. That’s obviously not going to have a meaningful source representation; there might be a class variable called SendImage, but the contents it happens to have in my image won’t be serialized out. Instead, I’d have to have the discipline to store the source image alongside the code in the repository, and then have a class method called something like initializeImages that set SendImage to the contents of that image file. In practice, this isn’t that difficult to do, and tools like CI can easily catch when you mess up.

                            Whether this is working against or with the image system is debatable. I’ve used several image systems (Common Lisp and Factor being two big ones) that don’t suffer “the image problem”, but tools in the ilk of Smalltalk or Self are obviously different beasts.

                            1. 1

                              Thanks for the reply! I wish I had more smalltalk experience. Maybe some day.

                        2. 2

                          Smalltalk does have Monticello and Metacello. I’ve heard good things about them.

                        3. 3

                          “Weaving code and brain” is why such languages/systems are so addictively rewarding to use, and why the small teams of weaved brains that use them are so much fun to be a part of. A small team in any field working together for a while tends to develop a shorthand language and the ability to anticipate each others’ needs and actions. These types of programming environments extend that feeling of domain awareness into the computer system itself.

                          1. 3

                            I think a good sweet spot is powerful language with strict static typing, so that you can evolve a team culture of designing for and coding to enforced public interfaces, but still get some of the benefits of lisp/smalltalk like control flow abstraction. reasonml is a good example of a powerful language designed to scale to larger teams.

                            1. 2

                              I recently started a new job using Typescript and React (this is the first time I’ve done any real frontend work after several years of doing systems programming), and one thing that’s helped sort of is the typed interfaces we have. The what and why of those interfaces isn’t always obvious, though.

                            2. 4

                              One thing article misses is that a standard coding style plus good documentation can knock out the problems it talks about. Also, selective use of macros.

                              1. 5

                                I don’t disagree in principle, but “good documentation” seems to me to be the equivalent of “don’t write bugs,” except that there’s fewer tools for enforcing good documentation - there’s no integration or unit tests for documentation. I’ve been enjoying Rust’s doctests, but I don’t think that really solves the problem. It still seems to me that it’s hard to enforce what “good documentation” is. Onboarding new people might be the best test of your documentation (a situation I find myself in now).

                                1. 4

                                  You will not enforce “good documentation” with machines in the near future for the simple reason that good documentation is a wetware issue, so you have to solve it at the wetware level. You can absolutely have good documentation at every step of the way, but it requires building a culture appreciating the value of documentation.

                                  Two simple steps will get you really far: the team refuses to develop a feature without a clear specification, and code reviews don’t pass until something is well documented.

                                  1. 1

                                    the team refuses to develop a feature without a clear specification

                                    Yes! Start doing this, everyone!

                              2. 4

                                I don’t think the size of the team is relevant. We lack ways of “weaving code and brain”, as the article says, in a way that scales. At its core the problem is about reification and communication of abstractions. Macros and mini-DSLs don’t encode the reasons for why the DSL looks like it does and you have to go and ask the original authors. It doesn’t matter what the language is if the implementation is the design then there will be scaling issues. So one easy experiment is to decouple the implementation from the design and see what happens. I bet it scales better.

                                Zooming out a little bit from code we can claim humans are just bad at collective decision making when the problem doesn’t fit in a single person’s head. Groups often create incoherent structures and most large engineering organizations look like one of two ways: geological layers of abstractions with teams sticking to their own layer or archipelagos that are far enough to make travel a hard problem.

                                1. 3

                                  Far as groups, it’s well-known in research that groups growing in size work differently than tiny groups of people that know each other well. The larger ones start needing more formality, managers, rules, etc. So, smaller can definitely be better if trying to stay focused, flexible, cheap, and fast-moving. The analogy is usually turning around a personal, speed boat vs a cruise ship or something.

                                  1. 2

                                    Yes, but I don’t like that analogy. More mass is harder to control but abstractions are not physical things. Big ideas can have “small mass” and vice versa.

                                    1. 2

                                      I was just addressing your first sentence. We have tons of empirical evidence that teams growing while working on same thing affects how that thing will be developed.

                                2. 2

                                  I wonder which of these beating-the-averages, brain-amplifying language implementations have been successfully used in today’s front-end environments (web and/or native mobile). Do people use Smalltalk or Common Lisp to develop front-end web or native mobile apps? Maybe ClojureScript?

                                  One caveat: If the language implementation encourages the use of its own UI toolkit that draws its own widgets, that’s probably a non-starter for many apps due to accessibility.

                                  1. 4

                                    It gets a bit tricker in the modern era with multiparadigm languages. I’ve seen small teams making really non-typical usage of JavaScript, for example, writing everything in a very Haskell-influenced functional style complete with lenses everywhere, from which they’ve built up in-house and/or project-specific abstractions to the extent that much of the code arguably constitutes an internal DSL. (Ok, when I say I’ve seen teams doing this, I mean that I know of one such team.) So a very speculative hypothesis is that today to find such teams you need to look beyond choice of language to how the language is used—people who might’ve been getting this kind of advantage in 1985 by using Lisp might today be just making very specific usage of multiparadigm languages like JS or Python.

                                    I personally do still like Common Lisp, in part out of personal interest, and in part because I’m an AI researcher with a strong interest in the history of the field, and it’s historically an important language for AI. But a lot of the claims of 10x productivity boost in earlier eras are comparing CL to languages like C and Fortran where it’s really not possible to write in anything near the high-level style of idiomatic CL. Even today there are still things CL does that are hard to replicate in other high-level languages, but I’m not sure the gap is as big.

                                    1. 1

                                      I guess Facebook Messenger written in OCaml/ReasonML proves that functional languages with good type and module systems scale much better than those without. ;)