179 comments

[ 3.0 ms ] story [ 243 ms ] thread
> Why does this happen?

Inexperience.

Come on, really? There are plenty of tools to keep code legible in any language: linters, documentation comments, design patterns, idioms from authoritative references, etc.

Every time I see code that doesn't really "fit in" with more experienced code in that language I already know it's gonna be wrong in other ways despite technically "working".

I've too suffered from the neuroticism, imposter syndrome, and egotism of being new at something, but damn can't we just admit this? Guaranteed everyone who isn't a developer sees it and has to deal with it even outside the code itself.

I've definitely been on the receiving end of experienced (10+ years) and competent (or, at least, clearly not incompetent) developers solo-developing inscrutable code that their colleagues had to pick up later. In contexts where they contributed code to bigger projects, their code showed more focus on readability.

I'm sure I've inflicted my share of hard-to-understand code too, most likely out of not wanting to spend extra time on some byzantine section of legacy code.

I’ve experienced this first hand. I think the trick is to encode the experience of writing the code. That takes different shapes in different languages, but a test suite that illustrates the problem and the solution is a good example.

You don’t just want the finished film, you want the making off companion piece.

This might be unusual but I would prefer to read someone's mental model of how the code works than the code itself. With thousands of files and thousands of lines of code it's difficult for me to work out how the code fits together.

If you document your mental model of how the code works, I can probably map the code to your mental model and understand the code.

I want/like people to create "entrypoint" packages/folders where entry points and component registries are found. Especially int main() {} So I can follow the control flow of the software.

I think too few people understand cornerstone technologies, including compilers and browsers.

> I would prefer to read someone's mental model of how the code works than the code itself.

I find this is often true for me. If there's a bunch of code that I'm having a particularly hard time understanding, it's usually because I don't actually have a working mental model of what the thing is supposed to do.

Problem is, with complex systems often the way to record the mental model is something like a design document, and getting programmers (including myself) to write thoughtful natural language to accompany our code is a constant uphill battle against business pressures to ship more.

I think the success of notebooks can provide a positive influence here.

They give text and readability a higher priority than traditional source code artifacts, and it really might only take a fresh IDE/plugin and comment meta-syntax to make that more normal in production code as well.

Agreed. It seems to me that doctests (i.e. code examples in docs that also count as tests, common in Python and Rust codebases) can possibly cover parts of this.
> Problem is, with complex systems often the way to record the mental model is something like a design document, and getting programmers (including myself) to write thoughtful natural language to accompany our code is a constant uphill battle against business pressures to ship more.

In my experience, the more common issue isn't that an initial design doesn't exist, but that it was never touched again after being approved before the implementation, so issues that came about when actually trying to implement it and the corresponding changes to the design aren't captured anywhere, let alone changes made during the ongoing maintenance after it's actually put to use. The only thing more frustrating than a lack of intent written down somewhere is an intent written down that doesn't actually match what's going on, and then having to try to track down if the intent _ever_ matched the actual design, and if so when (and if you're lucky, why) that changed.

Yep I absolutely agree. As the old quote goes:

"Show me your flowchart and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious." -- Fred Brooks, The Mythical Man Month (1975)

Its always mystifying to me how often programmers try to show me a new concept in their code starting with some complex functions. Start with your data structures. What data are you storing, why, and when? If you show me that, the code that actually moves that data around is usually pretty obvious. (And if not, your code will be 10x easier to understand once I understand the context).

Little sidenote:

people usually try to explain the appeal of typescript by "static type checking leads to fewer errors, etc."

conversely i think the real reason is that it can show you the shape of the data at every step by simply hovering your mouse over a function/variable.

typescript is great because it enforces documentation

> If you document your mental model of how the code works, I can probably map the code to your mental model and understand the code.

I agree.

This is something that I tried to do with my best project. [1] Only time will tell if I succeeded because it's really hard to document a mental model; it's like trying to explain to yourself what water is when you're a fish. [2]

[1]: https://git.gavinhoward.com/gavin/bc/src/branch/master/manua...

[2]: https://www.theguardian.com/books/2008/sep/20/fiction

This is great! I don't know if I'll have a project that I expect to last long enough to worry about bus factor, most of the work I do is either throw away stuff for myself or already part of a large shared team effort. But I love the idea. It's long but I skimmed it quickly and I think it's got a very good level of detail and is overall very readable with just the right amount of snark. :)
Thank you so much!

I wrote it in snatches of time nearly always late at night, so the snark just...happened. I was worried how it would be taken, so thank you for the encouragement, including about the level of detail.

I love it, I think I’m going to take some of these ideas for a literate programming thing I’m working on. Also props for proving that jagoff wrong.
Level of detail is something I think a lot about lately. Interviews go off the rails with candidates who answer in too much detail or not enough. Meetings go off the rails with non-actionable proclamations or tedious line-by-line details.

Machine generated / doxygen / javadoc documentation is the worst. I'd rather read the code, but code can also be difficult to read even if it's functional, especially when it's optimal. So having a guide to the code written by someone who knows how it is all laid out is pretty much the best case scenario.

That's some really handy documentation, thank you for sharing!

I like how you document each file.

I do this for all code I add devs to or hand off to. For instance, startup code I walk away from and hand over to contractors. A markdown doc with some high-level concepts + a hierarchical bulleted list explaining the module structure goes a long way.
it's amazing how uncommon this is in the big business world. Individuals have pride, cogs do not. I've been in both camps. Currently I'm a cog because it's better for my life at the moment. I'm frustrated by the lack of simple documentation - but at the end of the day I'm not really interested in fixing it.
I’m sure there is a real name for this, but I tend to call it a “Document of Intent”. It isn’t meticulously trying to explain every nuance, it’s a coarse treatment of the purpose of things and what they’re meant to do.
I started doing that with my own project after I reverted a (correct) change a few weeks later because I forgot to write down the original reasoning anywhere. No other contributors so far, so I can't say how much it helps on that front, but it has helped me a lot and I feel safer making big changes without fearing I'll break anything.

The entry point docs are here: https://github.com/trane-project/trane/blob/master/src/lib.r...

The bigger problem is when the code works (ish) but it’s clear that the lone developer didn’t even have a mental model of how the system works.

It’s usually very easy to see in a codebase when someone was flailing, and they wouldn’t be able to explain how or why this line of code works any better than you could. They just have strong conviction that this line of code needs to be there, because it was there at the stage in their flailing that things started working.

And to be clear, this can probably happen just as easily with a codebase with multiple developers.

Agreed, that sort of documentation is pure gold when done well.

It's something I always try to pay forward by doing in my own code. For example, one of my own lone-developer projects was an STB-style single-header <canvas>-like rasterizer library for C++. I started the implementation half of the library with a short overview of the rendering pipeline's dataflow and the top-level functions responsible for each stage:

https://github.com/a-e-k/canvas_ity/blob/f32fbb37e2fe7c0fcae...

> This might be unusual but I would prefer to read someone's mental model of how the code works than the code itself.

I prefer doing easier things rather than harder things also.

That's why I hate Spring and similar technologies. They autostart things everywhere. Even from library jars. So terrible.

If I would write Java program for myself, I wouldn't use Spring. I would use dependency injection, but I would construct all objects manually, calling their constructors and setters. I did that, it works, it maintainable. Some repetitive code, but it's so much easier to reason about compared to reflection monsters or autogeneration nightmares.

> This might be unusual but I would prefer to read someone's mental model of how the code works than the code itself.

The hypothesis under Peter Naur's (the N on BNF) "Programming as Theory Building" paper is that transmitting (by talking) the mental model is far more important than other format of documentation to understand a system.

"Beware of a guy in a room." --Jim McCarthy

Software should be developed by teams, period. Ideally with pair or mob programming, but with some sort of code review process for each commit at least. This is how you knowledge share and prevent understanding from being siloed in the mind of a single developer.

A good example from the open source world is the works of Fabrice Bellard. Fabrice has a tendency to one-and-done his legendary feats of coding, moving onto the next thing after he'd written something he deems complete. For projects like ffmpeg and qemu, maintenance teams have stepped up to keep them alive but the first, daunting task is always figuring out how the hell he did everything.

> Software should be developed by teams, period.

> Fabrice Belladd … legendary feats of coding

Maybe you didn’t mean to be so absolute in the first sentence? Our field is an art. An unmediated connection between one’s inner process and the code one writes can produce very compelling work that stands apart from others. In some cases, that work can prove “legendary”. That it may later take some support, documentation, study, and unwinding to make it maintainable doesn’t mean that the solitary effort shouldn’t be pursued in the first place.

A lot of celebrated software began as a guy in a room, and later was adopted by others because they thought it could solve their problems. The software never would have existed in the first place were it not for the lone person in the room.

unix, linux, git, perl, python, c++, javascript, clojure, grep, winamp, ...

If software is constrained to resemble the communication structure of the organization that produced it, well then programmer politics are _boring_, while an individual's mind is _novel_.

That's a recipe for the worst code base. Pair programming is designed to level up someone while bring down the other which is helpful in some situations but not in most. If you have two people who know little perhaps together they can produce something. Large teams break up tasks so individuals can work on pieces that fit together. Imagine a team of 20 sitting around debating that next line.
Pair programming is designed to level up someone while bring down the other

Wow, that is not what pair programming is "designed to do" at all. I never felt like pair programming brought me down. One of the best ways to learn something is to try and explain it to others. I am not talking about pair programming where one person just types like a robot and another one explains what to do. I'm referring to the situation where two people work on a problem that one of them is having. I think both participants benefit. In explaining the problem you have, you learn. In offering possible solutions, especially ones that don't work, you also learn. When you solve it together, you build teamwork. I really don't see any downsides. Especially with remote work, it's a great way to share knowledge and build rapport with your team mates. What real project is specified to the point where all programmers are interchangeable and can work separately without EVER talking to each other?

Yeah, not all people work optimally that way, and many of those that don’t do uniquely good work regardless. There’s room for all of us.

> What real project is specified to the point where all programmers are interchangeable and can work separately without EVER talking to each other?

I have no idea what this has to do with anything that anyone said here. But lots of projects don’t require a lot of ongoing collaboration. You may just be working in a very particular sector if you’re kicking around all these narrow absolutes. It’s a really big industry with lots of different practices and projects.

Yeah, not all people work optimally that way

I agree it may not be optimal for everyone, and I might be more social than some. I've just learned a lot working with people who knew more or less than me at the time and some of my more fun+stressful experiences involve spending hours in collaboration fixing a problem on a tight deadline.

I have no idea what this has to do with anything that anyone said here.

The original article talks about a hypothetical single developer project vs one with code review/pair programming and suggests that mob/pair/reviewed programming leads to better outcomes. I didn't think about solo open source projects which might be great work by one person, more like things that a business is built on which might be the responsibility of only one developer. I work on commercial software mostly for small companies with over stretched development teams. I've seen this kind of solo project built many times, and after the developer leaves it's abandoned or rewritten, which is just a huge waste of time. So "optimal" in the larger scale.

I do like pair programming, which I would define very loosely as real time collaboration on a single shared problem whether that's in person, in a zoom call or just over slack. I was trying to imagine a software development situation with multiple programmers and NO collaboration. Punch a clock, pick up jira tickets and never talk to a peer? I wasn't presenting that as a realistic scenario.

I admit I am prone to hyperbole, but however you define pair programming or code review, I think all software benefits from collaboration between programmers, users, designers, architects and product/business people. One of the more intense forms of that is pair programming, and I happen to enjoy it.

I might need you to explain how your API works, but I cannot write code while I’m listening to you talk, I need peace and quiet (or at least headphones).
Oh, absolutely, I can't listen to a book on tape and code. I mostly listen to death metal or electronic music with no (intelligible) words. I do think programming engages the language portion of the brain, which is why it can be so rewarding and draining.

But traditional pair programming, like actually sitting next to someone at one computer? I honestly enjoyed it! I might just be old? :) These days IntelliSense is effectively my pair programmer. TypeScript and VSCode allows me to actually enjoy front end dev work for once, fixing the squiggles is fun. But in the old days before Stack Overflow, time spent co-working next to someone who really knew an API or a library or a language was very much worth it.

My current team does do a shared screen pair programming thing a couple of times a week, there's a video call that we can drop in on while we are working on something.

Maybe it's more inspired by Twitch live coding and less like traditional pair programming but I still think it counts? It wasn't even my idea, but I participate because I enjoy it.

> Software should be developed by teams, period.

I disagree.

I have a project developed 96-98% by myself (depending on how you count) that is shipping with FreeBSD and Mac OSX.

It's not common for that to happen, but it certainly can. And for the programmers that work best alone, sometimes they will pull off something nice.

(comment deleted)
(comment deleted)
Mob programming is such an asinine concept to me. It's like all the people saying holotropic breathwork cures cancer. It's just developer mysticism. There is no amount of himing and hawing that would get me to believe treating one guy, capable of his own expression, as a code-input robot while developers around him bark commands at him is actually productive. Each line of code is so expensive and the probability of any one of the people barking the code remembering how something works is pretty low. There is significant evidence the action of typing/writing something produces better memory.

It works when you're helping a junior. It's insulting to anyone else. Part of our jobs as programmers is to be able to tease apart another developers mental model and learn the abstraction. If you cannot do this the codebase is either unsaveable or you are. The only codebases I have been unable to untangle in my long career have been either written by contractors or by a pseudo-intellectual moron developer that believes they are the second coming of Turing. Everyone else's code is usually able to be parsed with some patience and a debugger for more complicated tasks. I even gasp use pencil and paper to keep track of code flow often.

> as a code-input robot while developers around him bark commands at him is actually productive.

Nothing to do with mob/ensemble programming. At the end, is a discussion that a) make code easy to understand b) create shared knowledge c) avoid unnecessary work.

* If you don’t treat maintainability as a requirement, you usually won’t write maintainable code.

* If you don’t know how differently other people model ideas, you usually won’t write maintainable code.

* If you don’t know how differently you’ll model ideas in the future, you usually won’t write maintainable code.

There’s nothing about being a lone developer that means you won’t write maintainable code, but gaining the perspective necessary to do so requires either experience or feedback. So the lone developer problem is only really a problem for early-career developers and incurious ones.

> Relatedly, it’s it’s easier to have bad ideas if you don’t have to explain them.

This resonates a lot.

So many ideas that seem to make sense at first (you’re having the ideas in the first place after all), but start crumbling even with simple rubber ducking. Taking distance to try to explain if to a third party, even if they don’t exist, is underrated.

Funnily enough, writing comments and documentation don’t trigger that switch. There might be some hidden assumption that the reader is supposed to “get on board” and come share the same mental model perhaps.

Except that rubber ducking is literally a way to be a lone developer. :)
I have had experiences that contradict this! At the same company there was a ~30kloc frontend built by one developer and a ~20kloc backend built by four developers and the second was much harder to maintain and improve. Multiplying developers on a project can enforce some rigor and extensibility, or it can simply multiply the amount of time and effort available to just hack the next feature in. A single developer can create a mess only they can understand, or they can end up self-enforcing rigor and extensibility in order to handle the scale of the project. (Multiple devs certainly increases the oversight so you’re more likely to know if a mess is being made, so there’s something to be said for this approach).
(comment deleted)
Part of the issue here is most people do not know how to actually attack a problem together in a non personal way. What I've noticed is one of the following issues:

Can't accept cristicism of their solution. Too much ego.

Don't really care about finding the best solution.

Can't communicate well enough to discuss the problem at hand in the moment.

Don't have enough domain knowledge to add meaningful feedback.

In essence, a lot of features are built by the lone developer. The only saving grace is the process, breaking up the a task into smaller chunks and code review.

Does not fit my experience. Best software I've seen was always single good developer. Plenty of great single-person open source software out there to prove it.

The problem is - good devs are actually very rare. A single not so good developer doesn't get any feedback, yet they have all the clarity and context in what their code is doing, so they can take their terrible code quite far.

Obviously experience will vary from person to person.

But your point is well made. The quality of the code depends a lot on the quality of the programmer. Better programmers write better code.

Of course very few of us are great programmers on day 1. we learn and get better. I'm spending a reasonable amount of my time now re-writing code I built 25 years ago.

Equally, we grow better, and learn faster, when we get feedback. All too often the lone programmer is not reading code written by others, and is not getting feedback on his own code. So growth is slowed, or in some cases stopped for decades. Bad habits from 20 years ago still exist because there's no-one to rail against them.

So yes, lone programmers can be great, especially if they are outstanding to begin with. But the vast majority are mediocre and need the assistance of peers, and seniors to grow.

On the other hand those that grow up under seniors with bad habits, who _enforce_ those habits, are screwed.

I have been a lone programmer for a few years.

Whenever, I'm doing bug fixes I dive deeper into "how someone else would approach this".

It is actually quite easy to keep improving on your own. I read books on refactoring, clean architecture, etc. as part of my daily routine. The time not spent debating with another dev is spent learning.

I guess as a lone dev it is easier to do no self-improvement and keep apps chugging along until something terrible happens.

This works if there's no time pressure involved.
This is key. Good projects are based on good ideas. Good ideas take a lot of time to develop, and often involves back and forth, iterations, friction and failure.

If you have a good data model and technical architecture, the code almost becomes good on its own, even if it’s implemented by more people that understand the model. The problem is that it’s irrational to spend that amount of time, and it’s directly opposed to incrementalist mainstream paradigms of software development. It’s the process that almost has to happen outside of companies, because management would never let projects be executed in such a way. But when there are personal drivers (either by the creative aesthetic types or sometimes the hacker tinkerer types) you can sometimes, depending on the domain of the problem, get some really coherent systems that make people go “this makes sense, why would it be done any other way?”.

To me, the story of git has many of those traits, especially when comparing to what existed before.

As a sole dev you are also pushed towards simplicity, because your time and scope is so limited.

So a larger team might build some intricate DDD-microservices architecture with a services bus and a complex SPA frontend because, why not? That's what everyone else does.

As a single developer managing multiple microservices or separate backend/frontend codebases is a lot of overhead (unless it's a learning project). You have to do the simplest thing that will work. So if you can get away with server-side rendering, do that. A monolith makes more sense when you don't have multiple pizza teams, and so on.

This is true - you may (possibly) end up in a scalability dead end, but you will likely have been far, far more efficient along the way.
I think the only scalability dead end you'll end up in is people-related. That is, you won't be able to scale further because it would require using patterns/solutions that balloon the complexity so much you now need a team to keep track of it, and/or it would require more money than you can manage handling on your own (e.g. you would need to turn your project into an actual business in order to fund it further - at which point you need to start hiring people, to either do the businessy bits for you, or the technical bits, whichever your preference).
Or in other words: Conway's Law makes single developer code bases less complex, since there's no communication and responsibility boundaries reflected in it.

That said, I've seen a lot of small teams create a ludicrous amount of microservices. One per independently working team is my usual heuristic.

There is a problem in the tech industry where people's pattern matching isn't terribly good. They will have worked for a big company, or have read literature produced by people working for big companies, and concluded that "microservices" (to pick one pattern) are the best practice, because it worked for $BIGCORP, or they expect to be $BIGCORP 2.0 at some point, so why not be ready for that?

But, as you point out, microservices are the result of Conway's Law and shipping your org chart, not necessarily the best way to build software all other things being equal. If your org chart fits comfortably inside a broom closet, then maybe you're not quite ready yet for microservices.

In Apple development, there's actually a couple of programming methodologies that were specifically designed to make the code more complex, so it can be farmed out to multiple devs.

In my experience, it's a real good idea to stick to writing basic MVC, when working alone, on a UIKit codebase. If I was working with SwiftUI, I would probably consider MVVM, but I don't really like to take it much past that.

my experience too - if I am the guy for that piece of software (supporting testing, fixing prod issues etc.) then I have all the motivation in the world to make my work as simple and effective as possible. No place for fluff just because its in the vogue and 'big beards' of industry talk about it.

Measure of success of software is pretty straightforward in normal situation - users are happy using it, they don't care about technical details a bit. That and efficiency via simplicity above is how to do this for a decade, instead of building 1-man cathedrals with practically inevitable results

One way to put it is in that single developer code will have more variance, when compared to code developed by a group. This variance has a range of effects, and will sometimes be positive, sometimes negative. Certain idiosyncracies are unlikely to survive work in a group, which can be good for the group. But in the case of a single talented unicorn who is inclined to write well-architected code, a group will inhibit that, as each individual pulls in different directions.
I’ve recently joined a team where the vast majority of the code used across different projects has been written by a lone developer. The code quality is quite good, however, nothing is documented thoroughly and it’s mostly understood by a single person.

We’ve started to code review all new code being merged to the the codebase, we’ve been pair programming occasionally, and I’ve also been writing documentation about how to use the code as I get a handle on it. It’s been helpful so far.

Please document your code. Please provide examples.

What does well documented code look like for you? For me if I look at a new (webapp) repo I start from the controller level and look at the services and models. Would a good swagger page count as good documentation? My worry with documentation is that it can lie but the code doesn't.
I agree with this perspective. Documentation outside of code is almost never worth it (you cannot capture all the nuances or current bleeding edge - only code can) and usually just an excuse for someone to throw up their hands and not understand something.

There is value in guides, and good comments. For a library all the external functionality should be described (including prescripticely). For a big ol code base? Read the code and follow references!

The good documentation I’m envisioning refers to standardized comments throughout the codebase (we’re working with cpp, so doxygen in this case), with code examples within the header showing how the code should be used.

I’ve found there’s so many custom classes through my discovery if this codebase that it’s not quite clear how to use the classes or where to start, so having code examples along with comments describing their usage is pretty useful.

It’s also nice to utilize version control for the comments, which makes them easy to contribute to, as well as hopefully inspires others to contribute their own thorough comments when designing and contributing new classes to the codebase.

Documentation regarding process, coding guidelines, and the likes are better suited for a platform like confluence or some equivalent. We do that as well.

One of my measures of a good programmer, is the ability to grok, debug, and extend code written by someone else, regardless of its perceived quality.
Took me two years but I finally have a glimpse into the previous developer’s reasoning. Maybe a couple years from now I can explain it all.
One way to mitigate this as a solo developer is to step away from the code for a while and forget about the structure. You become the facepalming teammate at that point and start doing a better job of documenting and organizing your code for the day 6 months from now when you need to upgrade something or fix a bug. Eventually you start building with less abstractions, and more clearly worded variables, and better folder structure. Unlike teams where someone can keep making mistakes that impact others, you are your own worst enemy if you have to keep dealing with your own code over time. The solution to incompetence or laziness is ownership.
If you work on a project long enough eventually you forget about how parts of your project work, and this automatically happens
I don't think this is really a "lone developer" problem. If you jump into any large-scale application codebase you'll be lost and confused, even if it was developed by hundreds of developers.

Maybe the reason people perceive this as a "lone developer" problem is lone developers usually share their code for free (or for equity), so anyone trying to get up to speed on it won't be paid for their efforts (at least not right away).

I have a different take on this. If my project somehow becomes wildly successful and is acquired, the next team is not going to want to maintain my code anyway. How do I know that? Because no one wants to maintain someone else's code. No one wants to deal with someone else's abstractions and bugs or anything else.

100% of the time if it's feasible, the next version will be a rewrite of all or a significant portion. Especially for relatively small projects in terms of lines of code, which all of mine are.

Now the fact that it's plain JavaScript and messy, console.logs all over the place, I would argue, actually helps that new team. Because it makes it easy for them to trash my code. Which they definitely will want to do anyway. Why? Because they don't want a back end in JavaScript. Actually they didn't want it in TypeScript either. They wanted it in Python.

What if it was in Python? Well chances are they wanted it to use a different Python framework. Or actually Rust.

But what if the front end was very very well factored React. AND the front end team loves React. Well sorry but it uses React Hooks which they hate. And they really switched to Svelte months ago actually.

I would argue the part that matters the most is just not having a lot of code. Which you accomplish by not reinventing the wheel. Another big part is having relatively small functions and low cyclical complexity, and descriptive but not overly long names.

So in my mind although my code is a mess on the surface and easy to trash, and could be organized better, it will be easier than average to rewrite they way the want. And easy to trash means less likely to be stuck maintaining someone else's code for an extended period of time.

What I care about is getting a usable product out that has features that provide value before I run out of money. I could do half the features and MAYBE if I am lucky they won't trash my code.. but they will actually be worse off because then they will be stuck maintaining a code base that actually isn't using the frameworks they preferred anyway.

This is extremely pragmatic wisdom and I love it.

I like writing legible code for its own elegance and for my own sanity, but given that you found your way to be productive, I admire your radically honest (and accurate) understanding of what happens to received code no matter what!

Thanks. Just to clarify, I do feel that overall my code is quite legible.
Good! Code that’s clear and well tested is easier to delete. Ironic.
This reads like a very theoretical perspective.

In 23 years of software development I've picked up or handed over, both solely or as part of teams (mainly the latter), dozens of projects across seven companies. Some of these were projects bought as part of acquisitions.

In only three cases was a rewrite performed, and in only two of those cases was it justifiable (both of which involved creating a new version of the product with substantially better capabilities, and on a completely new technical foundation without which those capabilities couldn't have been enabled, nor the commercial value they unlocked).

None of the acquired projects were rewritten even though they'd mostly been built by single developers.

The idea that developers want to rebuild everything from scratch is a pretty tired cliche. Maybe it's true in some contexts but it's certainly far from a universal law.

There probably also is a bit of a difference in the companies business context. A rewrite in my company needs to be argued for and needs a solid business case, I.e you need to estimate that the burden of the current code base is choking the dev team and that the rewrite is a better alternative to plain refactoring.

Essentially most rewrites I am aware of were either of really small tools or when a project had been written in an esoteric programming language.

Fully agree, always amazing how some can make up reasonable reasons and then based on no data argue it must be like that?

Ok, maybe unfair and it is based on some experience, but mine is that code sticks no matter how bad.

Most people and industry have grown up and fears a total rewrite unless it really really hurts, and this sometimes taken to absurd levels where every dev knows it should be done, but management is like: but it works, just add me that tiny (lol) feature.

On the other hand, single devs code is usually quite good, otherwise they wouldn't have gotten that far... and it usually also has some consistency in itself. I'd say 70% good, 10% too clever guy with code that noone understands but is amazing too look at, 10% too unexperienced student but still good enough too gradually improve on, and 10% bug ridden shit. Who cares about frameworks that much nowadays, except the to be feared teams described below (and unless you have to integrate and it is a conflict)?

But what has to be feared is the code base of a mediocre team with a lacking vision and a lacking lead, that's where the horrible code bases that need an immediate full rewrite for me always stemmed from (:

Something I’ve repeatedly observed in my career: the rewrite is started, but it never gets finished, and then the rewrite and the original coexist long-term

“Never gets finished” takes different forms. Two I’ve observed: Form (1): half the product gets rewritten in the new language/framework/stack, but the other half stays in the original - for whatever reason, rewriting the other half never happens. I once worked on a product where the UI was a mix of Java Swing, Struts, and Angular. The Angular UI opened the Struts screens in new browser windows. I wanted to switch to IFRAMEs to make it more seamless but never happened. For the Swing UI, you had to download and install it - I daydreamed about embedding it in the web UI using a JavaScript-based VNC client (e.g noVNC) but that never got beyond daydreaming. I suppose it would have made more sense to just rewrite Swing+Struts in Angular but nobody was volunteering for that painful grunt work.

Form (2): new customers go on the rewrite but existing ones stay on the original. Sometimes the easy customers get migrated (small scale implementations with few customisations), but the massive customers with heaps of customisations don’t. “If it ain’t broke don’t fix it”, and people are scared that the migration will go bad, the customer will get upset and you’ll lose them. Eventually they’ll churn, or embark on some radical change in direction that requires reimplementing it anyway, or maybe even M&A with another customer and want to merge their instances.

Sometimes this is called the “lava layer anti-pattern”

Another common issue is the product team just going crazy with new features on the rewrite. Which makes sense from a business perspective but turns it into a Frankenstein-rewrite and dramatically increases the chances of "never getting finished".
Ah, Fred Brooks' Second System Syndrome.
Ah, yeah, I've had something similar to that situation as well: I worked a place where you could see the different layers of technology, like different strata in rocks (or layers of lava, to use your metaphor).

You could see the SQL Server 7 era database with tables names taken directly from AS/400 mainframe fixed field width text files. Then you could see the mid-to-late noughties era SQL Server 2005 tables, as part of the same actual database, with its more human readable table and column names, but it was effectively a copy of the old database. All the business logic was triggers and sprocs.

And then the latest iteration, which I was working on, had been broken into "micro"-services, with our service using a Couchbase cluster (which, by the way, was a bag of spanners, and which I will never use again if I can possibly help it) to store yet another partial copy of the same data, and then all the business logic at the "application layer" in the closest thing I've ever seen to an actual seven layer architecture. They were pretty keen on enforcing those layers so what you had was just a ton of boilerplate function calls. The cognitive load of understanding what was going on was completely unnecessary for a system that basically just stored names, email addresses, addresses and phone numbers. Actually it didn't store addresses: it stored references to addresses that were stored by a different micro-service. These kinds of decisions meant that most function calls that actually did anything were also RPCs. You can imagine how this all performed.

Latterly I discovered that the blasted AS/400 mainframe system also still existed when I had to work with some files it was spitting out, and had to write an EBCDIC to UTF-8 converter (not actually difficult at all) to do so with 100% reliability.

The main reason for all of this was dependencies: to retire any given iteration of the system you'd have to migrate all its dependencies (of which there were many) to use the new variant. This sometimes meant we still had to sometimes extend the capabilities of the old versions of the systems, even though almost nobody understood them. So yours truly ended up having to write a stored procedure that updated a bunch of both the SQL 2005 and SQL 7 era tables to get a particular business process to work, and this sproc was called by our new microservice.

Even with the number of people they had migrating dependencies was a huge undertaking that they were only just starting to look at when I decided to tap out. An interesting project and a valuable experience for someone, certainly, but something I realised I didn't have the patience or fortitude for. They weren't idiots though: they knew they had problems, and that they were being choked by complexity, but there's no easy route to getting out of that kind of situation.

It’s a function of experience. The more experienced you are, the less you want to rewrite. Sometimes you start in an experienced team and this comes for free.
(comment deleted)
(comment deleted)
That is not my experience at all. People are reluctant to rewrite it all due to sheer amount of work and people who take over others software keep maintaining it. The rewrites I have seen happened after years of talking about rewrites. Or after the original code was utterly unmaintenable.

> Now the fact that it's plain JavaScript and messy, console.logs all over the place, I would argue, actually helps that new team. Because it makes it easy for them to trash my code.

That being said, your code is going to be rewritten, but you are not making it easier. No one needs you to write crap to be able to trash your code and people do not need to trash your code to rewrite it.

But messy code is much harder to rewrite while clean one is much easier to rewrite. Especially if your goal is different technology, having original clean means that you spend a lot less time puzzling over what it done.

In my experience even refactoring is difficult to carry out with stakeholder approval. A rewrite is extremely difficult to get approval for.

You're describing an interesting take, and it makes sense in theory.

Unfortunately I don't think it happens in practice.

Exactly. All I care about is getting things done quickly and moving on. At university I was taught about "maintainable code", but in real life, I have never seen such code.

I have inherited code written by teams and it was not good quality. I just rewrote each section when I needed to change something. This seems like a much better approach then trying to write maintainable code - write code that can be thrown out and rewritten.

This article makes an explicit assumption: that code that is easy for you to read at a glance is inherently "good". The article then makes weird quasi-moral judgements like, maybe if you are a single person writing difficult-to-read code then "maybe you just don't need to write good code". Code that is optimized to be easy to read often has lots of duplication and very little abstraction.

This is even a well-understood tradeoff, and is why people working as cogs in massive organizations are often expected to code in languages like Java, Go, or C, which all purposefully offer very little in the way of abstraction, so the code is somehow more "obvious" and easier to read by whomever comes into the codebase next and needs to rapidly make some kind of change without breaking stuff.

The problem is... this is actually really shit code. The more duplication you have and the less abstraction you have leads to way more places for bugs to creep in. Yes: having the weird unified mechanism that no one is used to to handle memory management or authentication might seem a bit annoying when you sit down to someone else's code, as they are effectively using a bespoke framework.

But, let's reframe that: they are using a "framework"! And it probably has some kind of internal logic, and was likely designed for a reason to solve a real problem! If you take a few moments to orient yourself in the codebase, rather than assuming "I don't understand this so I guess it is bad code" you might come to understand it and then appreciate the work that went into the abstraction.

The alternative to this is frankly code that I enjoy, as I'm a security engineer looking for bugs: I'm going to look for things that are duplicated for bugs that were fixed in one place but not the other or for correct--yet somehow slightly different--behaviors which lead to parser differential vulnerabilities; even just boilerplate: the chance you got it right everywhere is about 0.

>Code that is optimized to be easy to read often has lots of duplication and very little abstraction.

Absolutely wrong and people need to stop pushing this narrative or put their money where their mouth is and use assembly. The people repeatedly saying this have probably experienced bad abstractions and gross OOP spaghetti code and improperly generalized this to "DRY bad, abstractions bad".

you missed the posters point. they are advocating for more abstractions, not fewer like assembly would give you. and who mentioned OOP?
Good abstractions are just really hard to find.

It’s much easier to sit down and start typing. And it’s somewhat easy to read that code and know what it does, line by line.

Abstractions inherently push away the details that enable this kind of lower level understanding. They have baked in assumptions that may or may not be explicitly documented.

Good abstractions provide leverage, but they are mini languages. They have their own vocabulary, their own execution model, extension mechanism and so on.

Good abstractions are clear and well factored pieces, but that doesn’t mean they are or should be easy to read without making an effort to understand their meaning. That’s not necessarily what abstractions are about.

That’s why tutorials and guides are so important. People need examples to ease into a new vocabulary and mental model.

Assembly (and JVM bytecode, WASM etc.) is very easy to read and understand. You learn these languages in what, an hour and a half?

But their vocabulary speaks about things you don’t necessarily care about when writing a web app or an ETL pipeline. People use abstractions to express something in a particular mental model or domain. Only in specific cases that means it’s optimized for ease.

It's not even well understood what abstractions are, in the first place. It usually ends bad when one starts with the premise of "I must abstract this, I must generalize this" etc. Most "abstractions" are terribly leaky.

I find it much more helpful to approach it like "This is what needs to happen, now how do I split this into different parts in a way that minimizes the interactions?".

I like how A Philosophy of Software Design discusses this issue you described:

> An abstraction that omits important details is a false abstraction: it might appear simple, but in reality it isn’t. The key to designing abstractions is to understand what is important, and to look for designs that minimize the amount of information that is important.

> Code that is optimized to be easy to read often has lots of duplication and very little abstraction.

I would disagree. I dont think you understand abstraction which is what Object Orientated Programming addresses. A class written once, deployed many times, which can include things like screen control resizing rules, or classes which handle reading and writing to disk or other things like that.

A class is a classic form of abstraction and thus can easily be read.

What I have not seen mentioned once in these discussions, is different coding styles.

I've seen UI design documentation but I've never seen coding framework's apart from Hungarian Notation in windows [1] but things like variable name formats, whether the code should be in classes, Routines, or Procedures.

Having worked on a number of different projects, I've worked on source written by single/sole dev's and I've worked on source written by multiple dev's from all around the world.

They all have their different styles of programming, they all have their quirks, their eccentricities and the current batch of programming tools including visual studio enables these differences which make debugging or securing code more difficult.

I dont think many managers or bosses understand programming either which is why the coding style is not something thats been mentioned. Even teams using tools like Github where there is discussion taking place with the code will force a group style onto the code.

[1] https://en.wikipedia.org/wiki/Hungarian_notation

I generally agree but for my own side projects, I like to keep a lot of code in one class if it makes sense to be there so I'm not jumping around a lot. For work code, I follow SOLID, DRY and 12 Factors so multiple people can be working in multiple classes and not usually running into conflicts.
Some create job security though ivory tower structures. A surreal masterpiece conceived while failing to cure their schizophrenia by licking hallucinogenic frogs while on LSD.

Others in small firms sometimes notice a "team" is not beneficial... especially if some have seniority, rotten intent, and a string of prior failed launches.

Finally, one realizes it doesn't matter, as you're billing by the hour. A grim apathy replaces ambition, as one starts to fantasize about being a plumber. =)

Ah, pipe dreams =)
This was a poorly written blog post by framing this particular situation as a "problem". It becomes a problem when someone acts inside a team or professional organization like this but the article is about single developer projects. There can be many good reasons why code quality doesn't matter in such instances but framing it from the start as a problem is more akin to clickbait. I would expect higher quality content on HN getting upvoted.
I basically do user studies on people using my modules and code. It’s so effective that I’m just perplexed that nobody else seems to want to do it.
Sounds like this theoretical lone developer doesn’t bother with TDD. Or documentation. Both of which are the antidotes to chaos.
I haven’t experienced this much, as a frontend developer. Not in my own code from years ago and not in other’s code on projects I’ve inherited.

I’ve joined some teams where the code was horrific after rounds of patchwork by people who didn’t understand the system. But by and large, systems made by one person have been really easy for me to work in. Except for the various cases of overengineering by senior engineers or when junior engineers try to apply too many ‘cool’ frameworks that aren’t necessary.

Maybe it’s because most webapps solve simple problems in simple ways. There are collections and entities and relationships and almost everything is a page or a list or a form.

Does this apply so much when there's model boilerplate, folder structure tooling?

If a lone developer does things like design documents, flow charts to explain infra and documentation.

I think the lone dev would be a problem if they didn't setup with a commercial interest. Not to mention time to setup everything properly instead of just getting things working.

So I've noticed the same underlying phenomenon here: looking at code I wrote 1 year ago and being kind of disgusted. What's interesting is it seems the author came to a completely different conclusion from me.

For me I didn't think "oh, so 1 man team === bad code" - I instead thought "ok so I have no excuse. I have to focus on keeping the code non-confusing and writing comments even when alone". I've found that if I put in the effort, I can write code that's less disgusting 1 year later than if I hadn't cared.

So if you ask me, I don't think there's that much of a correlation between number of people on a team and code "quality". I don't even know what code quality is, honestly, because it's not all methods being <= 10 lines like some linters might have you believe.

For me personally, I have the best time reading code with comments. The self-documenting code thing just hasn't panned out for me. And of course I don't mean "var x = 1 // set x to 1" style comments - I mean as others posted the kinds of comments that just explain the code author's mental models or reasons for doing things.

> I have to focus on keeping the code non-confusing and writing comments even when alone

This is the key. When I was younger, it was drilled into my head "be kind to your future self". My future self is either maintaining the codebase, or ensuring that someone else is - and the best way to be kind to myself is to make both of these things easy.

I comment my code, even when it's not a difficult segment. I try to capture my thinking process - in the most organizationally friendly way. It makes a world of difference even to me, should I edit my own code a year later. I try to install this into everyone I mentor.