Theory-building and why employee churn is lethal to software companies
baldurbjarnason.com
baldurbjarnason.com
This is ringing particularly true to me right now.
The organization I work for has allowed bit rot to occur for a number of years, to the point that it's becoming increasingly common for things to break whenever even seemingly small changes are implemented.
The "solution" that we've implemented is to require a detailed RFC document whenever you want to create or modify anything. You're required to outline every anticipated step along the way, all the tiny minutiae that you foresee yourself encountering and how you'll deal with it. And in a Google doc, no less.
It's utterly exhausting.
> The first is that it’s a partial explanation for bitrot, the phenomenon where working software seems to deteriorate even when the code is untouched.
Note that requirements change, and bad tests will break on no requirements. Telling the difference is an exercise for the reader.
1. Ask for forgiveness and do whatever you think is prudent regardless of org structure.
2. Find a better org structure elsewhere :)
The tests tell you what it's supposed to do, not how it does it.
They are when devs put all the code into those header files.
This means if you want to buy a struggling or insolvent company it will most likely have bad documentation and tests.
But one of the most common steps when buying a struggling company is firing people (making the company fire people before buying it) and the selection of who goes and stays is rarely a good one. And it sets signals which make it mote likely for further important people to quit.
Usually, when I see tech companies purchased, it's because the company wasn't struggling: it was doing quite well, but was small, so a much larger company bought it up in an acquisition to add its capabilities to their own, like the Borg. The big company gets IP, customers, revenue, and an already-working business, while the founders get a big payout.
I'd suggest just using the documentation. Manuals get used for doing testing and implementation. Using the documentation is like testing for it, and finding those problems right away is the best way I've found to ensuring that the documentation is correct and up to date.
This reveals the secret goal of documentation: To build that model, not to document every last detail. The code can do every last detail, it's the model that is hard.
Set out with that goal and... yeah, I can't guarantee success for you either. It's a fundamentally challenging task. But at least you'll be off to a better start than if you think the primary purpose of documentation is to document what happens if you set this combination of flags. That has its place too, certainly, but if I can only pick one, I'd take the high-quality high-level overview over the stuff I can read the code for in 30 seconds.
But like you say it is challenging, even finding good names is hard. But basically, can you write a story about the software working, that would allow readers to understand it? Create user-stories but not (only) about the users but understanding that any module is a "user" of its components.
A junior dev who comes in without much coaching, might just reference an anti-pattern from the "mess" section and replicate it throughout the code base. And then do that enough times, and a senior dev who gets brought in might conclude that there's no design at all, and that what it really needs is a rewrite.
Maybe a good metaphor is to view the code as a simulation of something, and the documentation as the description of what that something is and how it behaves.
For instance say the software implements matrix multiplication. It is easy to document if its "units" correspond to the well-known concepts from the theory of linear algebra. But if the code implemented matrix multiplication but used random names for its modules classes and methods it would be difficult to explain what the code does.
Any explanation must assume that whoever reads it has a basic understanding of the basic concepts the software calculates and manipulates. But as said it is challenging to come up with such a theory and that must be done when the code is written, so the code itself reflects that "theory".
Code documentation, in contrast, should be auto-generated from the code.
But yeah, totally into auto-generated (great example is dbt’s docs command: https://docs.getdbt.com/docs/collaborate/documentation)
At some point I got so pissed off about this that I started assigning code reviews to him - he contacted my manager and asked that I stop, because he had no understanding of the project in question.
What did this mean from the documentation side?
I would have loved to accurately document what I was doing and how the project was structured - but I actively avoided doing so.
Every time my manager's boss knew what I was doing, I was about to be shat on from great heights.
Months of work and planning erased at the drop of a hat - time and time again.
Dependency injection? Fuck off. Spring's too heavy.
Modularity? Fuck off. The project's too small.
Hierarchical profiles? Fuck you, keep using a properties file (with an increasing number of dynamically generated keys).
I was constantly asking for a new developer to be brought in for the transfer of knowledge - at best I was told "That's not a bad idea."; yet nothing was ever done about it.
I took an unmaintainable pile of bugs and made it stable and maintainable. But nothing I ever did meant that I was someone worth listening to.
Eventually I got so burnt-out (and consumed by feelings of uselessness) that I walked away without finding another job.
I was damned near suicidal from fighting for acknowledgement.
I cannot imagine that my departure went too well for them.
That's the thing about places like that.
nobody will care if you leave, nobody cared about the last person that left, they have other priorities and software engineering is not one of them.
if its that much of a problem, their business will fail and some other business that does care will take over their market. but that might take decades because software engineering isn't actually core to a lot of business activity. youd be amazed at how bad some software is for major companies, and yet they are very profitable.
you only have control over yourself not other people.
My departure would've been less than ideal.
Either way, the company's haemorrhaged talent (now including long term CEO and long term CTO) and the stock price has plummeted since I left.
(In no way am I crediting myself for this; at best I was a canary in the coal mine - but I take my solace in schadenfreude.)
that describes pretty much every tech company in the past 18 months ;)
Whatever decision you made they yelled at you. I got yelled at once for not lying to a government official. When I quit, every sr. developer quit within 3 months of me; 7 total left and left behind 6 non sr. devs. This was 3 years ago. The company is hemorrhaging money but just keeps issuing more stock to stay afloat. It used the money from that stock to buy its competition in a very niche market and essentially just put them out of business. So they survive and lurch on.
I ask about ‘points’ in interviews, its an immediate stoppage for me
I asked him why he was so upset. Eventually he admitted that closing tickets in jira was how upper management measured how well our team was doing - and by extension him. I thought that sounded like his problem and still refused to put the issues in jira. So he did it - meticulously creating a couple dozen issues, making up a BS point estimation and closing them all as complete.
Whenever I think about bullshit jobs, I think about those silly cost estimation cards, that guy, and how deeply satisfying it was that day just diving in and making our product actually better.
- Why the commit was made should be in the commit message
- What the change is inherently lives in the diff
- Why the code is how it is should be in comments adjacent to the relevant code item.
There is some tiny marginal benefit to what you’re talking about (there’s a chance someone might later want to look at the corresponding issue). But you aren’t acknowledging the cost, measured in my sanity making a bunch of one-line issues in jira, and the team’s time doing cost estimation.
It’s nice to leave breadcrumbs for future code historians. But our job is shipping product. Cost estimation, jira, etc all only have value proportional to how much they help us ship. If the meetings are pointless, and jira isn’t needed then doing that anyway is a waste of time.
You're lucky not having been discussing money budget per ticket instead.
I have had projects where sprints got money budgets assigned to them, which divided by the cost per hour of each developer got us the amount of hours available for the sprint.
Those hours were the budget for the sprint, no more no less, better chose wisely the stories that fit in.
To make it even better, some project activities only were given such kind of budgets in specific times of the year, then one of the external consulting partners would have the go at that specific sprint.
So not only were the tickets chosen to fit the budget, every few couple of months, the teams doing the work were always rotating.
But when they become the subject of endless meetings it's a tell that maybe the company has hired too many people too quickly and is in the "bullshit jobs" phase where it's happy to waste precious salaried hours on meaningless debate.
I have had your experience with poorly organized PM and horror stories during sprints...
And I had the best planning sessions with a proficient PM, that made picking up tickets a marvellous pleasure.
+100.
Booking.com and JIRA come to my mind as good examples. Just about everyone complain about Booking, customers, engineers working there, hoteliers etc., Yet it continues on. I hear they've been trying to migrate to a modern tech stack for years but to no avail.
I am reaching 50 years old and am yet to see any ticket management system that is as powerful as Jira, while offering a relatively good usability story, and the integration story of having a ticket bind to documentation on confluence, SCM related history and CI/CD reports.
Likewise the competition to Booking usually fails to show as many properties, have as rich filter selection, and in many cases their prices are higher than what Booking offers, moreso if one happens to have Genie level status.
The irony of jira is the years it takes to realize how few pieces of software can soak into complexity like it can.
The pain of jira is how it largely can’t universally handle complexity out of the box or how you’ll find yourself in it - how it must be actually setup for your use case - how much it benefits from being setup by someone who has experiences with the pros and cons of cocaine one of the many ways of doing something.
The new project type (team?) is a step forward to start with from scratch. But a kiss of death if you need to outgrow it for some features that aren’t there yet.
Unless you're a mega corp - you're not getting a good work management solution.
/Softwarecompanies/Some companies/g
I wrote a paper for an assignment (maybe ten years ago, ask me for a link, and I'll see if I still have it), in which I called this phenomenon Corporate Inertia, and it isn't only applicable to software companies.
Sometimes the inertia is good - it allows the company to weather bad times.
Other times the inertia is bad, because the company cannot pivot in time to address new challenges or take advantage of new opportunities.
Mostly, the good outweighs the bad - if a company cannot pivot in time because it is so large, it merely buys the smaller company that verified the idea already.
First week was rebooting servers at random to try and get things working while I mapped everything out.
Eventually rebuilt everything and put together a good team.
Key was learning to tell management no. And having someone to keep upper management off our backs.
I was in a similar situation once, I spent months telling management that I was the only one keeping them afloat technically and that it was a critical risk to the business. They responded by trying to pressure me into unlimited unpaid travel and overtime.
I left and they fared poorly.
Don't fight management, leave.
Maybe this is true in the general case, but there are so many exceptions to this that I feel it is not true enough.
Customers don't care about about using the best tool, they only care if it is good enough, and a computer/application/software that crashes once a day is actually good enough for most desktop workflows.
An OS that abuses the user might still be good enough that better alternatives cannot get a foothold. Same is true for web applications, enterprise systems, games, etc.
It's only when you get into the embedded space that you face the "quality or GTFO", because people have lower tolerance for malfunctioning appliances than they do for malfunctioning computer systems.
> I was damned near suicidal from fighting for acknowledgement
I sincerely hope this was hyperbole. Please don’t ever allow anything work related to affect you to this extent. You are more than your job. There are other opportunities. There is much more to life than software.
> I took an unmaintainable pile of bugs and made it stable and maintainable.
Same here, except it was supposedly using all best practices e.g. Spring Boot, DI/IoC, Mock testing framework, YAML configs, extremely elaborate package hierarchy and so on. The only thing it did not have was working solution for the business problem it was written for.
1. The developers who create the software.
2. The rest of the company developers work for, and possibly other people in other companies - who all make money selling or renting the software.
3. The actual users stuck with the software.
I'm not sure if Agile meant 3 > 1 and 2, or just pretended to, but actually meant 2 > 1, but your comment does read as advocating for 2 > 1 > 3, which is arguably the current industry standard.
I think two things play into this: Layers.. it is just easier for people to add layers around something, not touching and understanding the original code and doing it right there. And then profs or someone else always says layering is good! (It may sure be, or also sometimes such "hacks" are the right thing to do, but if that is always the default mode... uhh oh).
And second, university training: OOP, design patterns, software layers, all claimed to lead to structured good programming automatically still taught in 2023, which is just not true. By untrained or unexperienced personnel leads to worse than 80s spaghetti code..
Just yesterday I exercised with relative who is in university for exam.. Java, design patterns, arggh. Imo it just makes no sense at all to try to teach students inheritance and design patterns with Cat/Dog/Animal et al, it is so backwards. You need to program for a while bigger real systems to see some patterns emerge and their usefulness, and then can relate to the formal way and maybr use some patterns reasonably. But starting with those on toy examples just leads to what you describe: People going in with their trained knowledge and applying that to everything without rhyme or reason. What could be a simple five liner function already becomes a convoluted abomination. Just one concrete example, but this then on many levels of software engineering.
Who or what will save our future? :D No, it won't be ChatGPT. (Second lol btw, relative really showed me how likely all of then may use ChatGPT i. their online exams...)
Most people do not have the memory span to work with mutable state, so something simpler should be the default.
1) Proper FP languages with good enough semantics and necessary features to write functional code effectively. Those languages all fall into category of "too difficult" for the majority of industry, which seems to suffer from severe allergy to learning anything beyond "Intro to programming in Java 6" freshman class.
Even things like lambdas only recently stopped being too complicated for a typical programming shop that follows well-known best practices, such as "Programs must be written for inexperienced non-programmers with no prior exposure to the problem space to read, and only incidentally for machines to execute.", and "Would somebody think of the juniors?". Closures? Nah, the zeitgeist is still on the fence with this.
Yes, I have a sore spot for this. I've heard this reasoning many times in real life, and once was even target of it - an otherwise competent programmer and a good manager once asked me to stop using lambdas for event handlers in Java 8 (which the company had only recently adopted), for the sake of the juniors who won't understand. In fact, he meant specifically his favorite evergreen junior (who's been in that company a couple years longer than I was then). But guess what, couple juniors actually asked me to explain the how-and-why of lambdas to them and got it near-instantly. Said evergreen junior probably spotted the lambdas in the codebase too, or otherwise got interested in this, read up on the topic independently, and three months later was evangelizing basic functional programming in Java and otherwise upskilling his fellow coworkers.
But yeah, it's too difficult, your co-workers won't understand. Anyway, where was I? Oh right:
2) The other, arguably larger, kind of FP adoption: library-level and code style level implementation of FP features in languages with little or no natural support for them. This doesn't mean people understand FP extra well - they're barely aware of it at all. But every now and then some library gets popular - say, a sum type for error handling (typically known as Result, Either, Expected, or something similar) that's "obviously" "better" than exceptions. Except, that library is doing a half-baked implementation of a concept (e.g. monads) that doesn't fit the language, and the users don't understand what the approximated concept is, nor are they supposed to try (remember, FP is too abstract for a freshman after "Java 6 101" class).
The result is, as expected, an ugly mess in the codebase, displaying all the bad sides of imperative and functional styles, and leveraging none of the good sides.
Anyway, what is my proposed solution to this? For the industry to stop hating education and professionalism. To embrace the idea that the job of a junior is to learn, not to be the yardstick for complexity of the codebase. To encourage a culture of learning, of professionalism, at every level. To stop dismissing or banning techniques that reduce size and complexity of the codebase by large factors, just because one has to invest a couple hours of learning up-front to be able to use them. Etc.
</rant>
I was dealing with having to support releases across multiple historical branches, and wanted to separate the core and business logic to allow for easier support on the older branches.
I needed the dependency injection in order to break up cyclic dependencies across modules (without which the core and client logic could not be broken into separate repositories).
Internally, the deployment tooling treated the profile as hierarchical; when I took over the project it was already using dynamically generated keys to store trees of data in the .properties files it was using for user profiles.
It only became more hierarchical as time went on and the internal representation fully so - using a tree structure internally allowed for very clean subtree selects (which helped to clean up an entire class of bug, introduced on mass, during a particulary ill advised (and strongly advised against) piece of work (completed (despite being given without notice) in a single iteration - when it really needed at least 2).
I would still have preferred to use an external format that allowed me to specify or generate schemas for the user profiles, and it also would have helped with supporting profile versioning (which would have been valuable for company internal users).
Primary thing that I learned being a consultant - you can leave and you don't have to suffer the disdain from the management. You learn to disconnect yourself and be replaceable.
It makes for a less frustrating life.
Sad that it happened to you. Don't invest yourself in someone else's child.
I believe this is partially due to lack of training (technical writing rarely gets trained, and companies rarely invest in it as a skill), and sometimes probably also due to lack of exposure to high-quality documentation (which serve as role models you’d then want to live up to in your own documentation).
In addition, companies often do not reward expending time and effort on writing documentation, because it isn’t crucial in the short term. It’s similar to tech debt, just documentation debt.
While writing good documentation isn’t easy, the fact that it usually gets neglected is a cultural issue.
People "on the spectrum" are not great communicators, and also struggle to understand what it is other people know and don't know.
Also, documenting is genuinely hard, even if you don't consider that it changes all the time. Which it typically does.
Not that I disagree -- working in highly technical fields this long, I could categorize 80% of my co-workers as lacking typical social and communication skills and you never know the real reason. It's to the point where I see women get promoted to management roles more often because they're superior communicators.
That whole process sucks and I’m sorry you have to go through it. It’s probably the best solution of the time. My one question is… Isn’t that why we have github issues?
Write your RFCs as markdown in a git repository. Stick it to the man!
Documenting a terrible project is an excellent way to propose and volunteer yourself into a new project of your own design and ultimately get promoted into lead/architect/SME types of roles. I don't know anyone who has done it any other way.
Documentation can describe a system state, and how to use or approach it, but in my experience it really lacks in capturing the full depth of experience when it comes to interacting with it. All the little shortcuts and workarounds. All the debugging steps. Seeing something, and realising you've seen it before and solving it in 5 minutes instead of the entire day it took you last time. I could write a story about that day, but it wouldn't do anyone any good - they won't learn from that experience like I did and it won't spring to mind instantly. Assuming they even know where to find a piece written about their specific problem.
So when someone says "How do I X?" all I can think about is all the incidental things I've learned when trying to do something else. This builds the bigger picture. I can answer your question, but you'll just paste the code in and be back to ask more tomorrow. I valued the coworkers who treated such things as more of a discussion and learning opportunity than a transactional question-answer routine.
This way you can incrementally automate various pieces of it so that gradually the user is doing less and less, but there's continuity on their end: run the script and do whatever it says to do next.
Although the example uses Python, rather than shell.
A tool like Jupyter Notebooks would be great for this - comments & code can be placed into a single document. I'm working on a project called unSkript (https://unskript.com) that is built on this idea.
This is the part that most people conveniently forget. Many people find it easier to create elaborate docs and processes than to regularly interact with the software system they've built.
- A defined and documented process
- Project planning or organization changes
- Staffing, purchasing, or contracting
These are fine tools for a lot of business problems, but they are rarely helpful, and often harmful, for software development.
waterfall by any other name smells just as bad.
I think the reason is that senior management has no trust in the developers, which is the ultimate cause of the need to have waterfall. Each reporting level wants to show concrete evidence that those under them are "doing their best", rather than to have trust that they are.
Of course, it is understandable that people want concrete "evidence". But waterfall is the result, and you end up with big-bang designs, RFCs that are out of date because a change in circumstances necessitate a change that is not documented in an RFC.
It is a management problem, not a software development problem, imho.
Just because it seemed obvious what you were doing and how things worked while you were writing the code doesn't mean it is, or that it will stay that way. Especially if that code ends up getting abstracted out. In line comments demystify code for the next guy way better than any amount of well written documentation.
As for what they're making you do with the RFC documents for changes, try writing your stuff as an ordered multi-tier list. The first tier should be the "manager" description of the change. The next level should be the more technical description of changes. The third tier is the location in the code where that change needs to be made (like a file path sort of thing, but you could use name spaces and even method names if you want). The forth and final tier if you need it would be cross references to other related items in the list and implementation notes/challenges specific to the file path. I've used this approach in the past myself. It's much faster, and it allows you to write a spec doc that isn't a huge waste of your time because it allows you to half write your code at the same time. It also provides a sort of semantic map from how the manager thinks about things to how you think about them, making it much easier for a manager to discuss it with you without having to write nearly as much.
Another benefit to this is you can often use stuff from the second and forth tiers to add in line code comments.
At least thrice I joined teams where all original developers and the manager was long gone. The situation for some reason looked very similar in all cases. The team is complaining about supposedly bad design that is preventing them from getting work done. But the system is also large and complex and they are unable to fix/rewrite it.
Lo and behold, in two out of three cases I found that the original developers actually did pretty good job designing the system but in all cases any one of new developers understands only small part of the design and is unable to contribute changes across the entire system, there is a core of the system that nobody understands even though everybody uses and the team has made changes that are against original ideas of the system quickly creating more problems and exploding complexity.
In all cases developers are only able to comfortably do some kinds of changes to the system that are routinely done (like adding new fields to forms, messages, etc.) that do not change how the system functions. Changing the basic structure of the system is a taboo.
In essence, developers treat "legacy" code as if it was some kind of framework or language runtime -- to be used but never to be studied to have its internals understood. (I will never understand this approach to development)
Making "non-standard" changes (yes, one team used this exact term!) pretty much always ends up with something resembling long archeological expedition with ancient traps, diseases and wild animals.
My currently company only did a specific niche in digital marketing but for sales goals needed to sell outside of that niche. Unsurprisingly, the product didn’t work for those use cases.
This is key. Most engineers want to build frameworks instead of applications. But leave the frameworks to the framework builders unless you were hired to build a framework.
I can jump into a 10 year old rails code base and be productive right away if they stuck to the framework defaults. But invariably someone read some blog posts about how "rails doesn't scale" and built some rats nest of service objects/decorators/portsAndAdapters wtfs and it takes some serious code spelunking to figure out what's going on (hint - the end result is always creating or updating a database record, or making a single api call).
I would argue microservices also encourage bitrot. You can leave some service running on some ancient libraries for years it doesn't need changes. But if you have a monolith where you constantly keep the dependencies up to date - everything stays up to date.
But this in my experience is truly important -- from the very start plan how a new member of the team is going to get really proficient at the system. Ideally, they should be able to immediately recognise/reuse patterns/idioms they already know.
People bitch and moan about boilerplate of Java applications. But when I join the project and I see controllers, models, repositories, entities, services, etc. I am pretty much at home as I know what all those things are doing and how they are connected to each other.
Whenever developers decide to run with their intellectual desires it ends badly.
I try to explain to people that typical business backends applications do not have custom problems. Most of those REST services are just supposed to receive query, run a piece of business logic, translate it into database call and then map response into specified format.
Mostly I’m thinking of more generic services rather than pure ‘business logic’ systems. These are the sorts of problems that Twitter might have in the future. Some of these are problems that Facebook might have gotten to discover in their last big outage.
Many probably noticed that very old, relatively simple machines, cars, tractors, printing presses, etc. can be maintained almost indefinitely. Many businesses do prefer old machines even when there are superior new alternatives and that is because the new versions are just too complex.
To keep maintaining something indefinitely you need to understand the machine enough to know every single piece that needs to be maintained and how to do it properly. Everywhere you need to lube it or clean it or to look from time to time.
Make something complex enough and nobody will have this knowledge. Things start deteriorating. Maybe the data in the database will start accumulating defects, maybe those defects start breaking processes, maybe the processes will not be fixed properly but the errors just get ignored possibly causing other data inconsistencies, and so on.
When you have people who wrote it and understand it fully they can very likely maintain it properly because they understand how to work with it, where to look, how to address problems. Loose them and if the system is complex enough the new crew may not be able to acquire this ability. And then it is just the question of how fast the system is going to be deteriorating.
At the expense of dependencies constantly breaking you, which itself contributes to bit rot by pissing off developers who find workarounds to pin their dependencies and/or switch to work on different products.
I was baffled by this as well for 15+ years until just last year I had a key insight: most developers don’t like to read. In fact, most people don’t like to read. Most people are intrinsically lazy and since growing your skills involves hard work, they don’t do it unless pushed to do so, either by life circumstance or necessity.
Time and time again people have asked me: “how do I get really good at $tech or software engineering?” I recommend a few books or send them my standard list of must-reads. In 999/1000 cases the person approaching me gives me that defeated look that I know means: “Oh. I was hoping you’d say something not involving working hard.” Most people are polite enough not to say it to my face.
When folks have problems with HTTP I ask them if they’ve read RFC 2616, and if not, I offer to bring them a nicely printed and bound copy. It typically goes unread though some use it as a reference. Only once did someone follow my recommendation and read it cover to cover.
Folks seems to lack a kind of basic economic perspective. Spend a week, month, something along those lines growing your skills (or even just 30 minutes reading the docs on that thing you just spent 2 hours declaring idiotic). Yes, this means probably giving up your favorite Netflix binge, video game, or drinking on weeknights in favor of study. But once these skills are internalized they pay back for a lifetime with good returns. Yet for some strange reason people prefer to keep thinking short term and so they never learn what they’re doing. It used to make me sad but I’m making peace with it.
Specs are extremely terse and dry, and I never liked reading them. I pushed myself to read a few and now I find myself craving that kind of technical documentation in my work. The WebAuthn spec is pretty interesting and also has some even more interesting drama on GitHub issues.
My reading list at any point is tied to whatever I am doing in my private or professional life. If my boss asks me to help grow the company my reading list will contain a bunch of stuff about company growth. If I have money to invest but no knowledge of investing -- you will see me listening to investing books while cooking, taking walks, commuting or long distance driving.
Basically, whenever I face a problem I look around for literature to help me with it.
I really hope to have an original problem in my life. Until then I assume that whatever problem I have there is somebody who wrote a solution to it and if I can't find it it is just because I wasn't searching hard enough.
People / Soft Skills:
* How to win friends and Influence People - Dale Carnegie
* Secrets of Consulting - Gerald Weinberg
* Peopleware - Tom DeMarco & Timothy Lister
* Nurtured by Love - Shinici Suzuki
* Turn the Ship Around! - David Marquet
* Never Split the Difference - Chris Voss
http://dhemery.com/articles/resistance_as_a_resource/
http://dhemery.com/articles/managing_yourself_through_change...
Macro Skills:
* Toyota Production System - Taiichii Ohno
* Workplace Management - Taiichii Ohno
* Thinking in Systems - Donella Meadows
* Fifth Discipline - Peter Senge
Agile:
* Extreme Programming, Explained (2nd ed.) - Kent Beck (the annotated bibliography is a goldmine)
* Chaos: Making a New Science - James Gleick
Programming:
* Implementation Patterns - Kent Beck
* Test-Driven Development, By Example - Kent Beck
* Refactoring (both editions) - Martin Fowler
* Refactoring to Patterns - Joshua Kerievsky
* Practical Object Oriented Design - Sandi Metz
* Functional Programming in Scala - Chiusano & Bjarnason
* Growing Object-Oriented Software, Guided By Tests - Freeman & Pryce ("The Goose Book")
* Modern Java in Action - Raoul-Gabriel Urma
* Java Concurrency in Practice - Brian Goetz
* Grokking Algorithms - Aditya Y. Bhargava
Architecture:
* Righting Software - Juval Lowy
* Release It! - Michael Nygard
* Site Reliability Engineering - Beyer, et al. ("The SRE Book”)
* Domain Driven Design - Eric Evans
* Introduction to Permaculture - Bill Mollison and Mia Slay
* Fun with a Pencil - Andrew Loomis
Design:
* All books by Edward Tufte, especially “The visual display of quantitative information”
* The Elements of Typographic Style - Robert Bringhurst
* Understanding Comics - Scott McCloud
* Design of Everyday Things - Don Norman
* Notes on the Synthesis of Form - Christopher Alexander
* The Timeless Way of Building - Christopher Alexander
I can’t tell you how valuable it’s been at times to understand how packets flow over a network. When ECONNREFUSED first rears its ugly head many developers tremble. Over time you learn it’s just a slog of debugging. Emotional control while programming is invaluable.
Also, the realization that people skills are actually no less important than tech skills in a tech setting.
Yes, and this is hard to accept: they are more important than technical skills. I have worked with many truly brilliant engineers who didn't see their ideas implemented because they didn't have good social/conversation skills. The most common version of this is the engineer who gets miffed/irritated/pissed off if people don't accept their ideas after 5 minutes of explanation. Meanwhile, the hustler whose code totally sucks gets the win because they know how to schmooze the business folks (or literally just tell the best lie).
Hard technical skills are not enough for most people to be successful. There are the outliers with "super programmer skills" (and here I'm thinking Linus Torvalds / John Carmack level status) who are just so indisputably expert that they should be heard. But even then they may not get the win. You have to know how to talk to others.
So, the way I think about it instead is that I’m just another type of system that can be maintained, improved, evolved. And my communication with others is simply an interface. And, well, any engineer can grasp the idea that easy to use and well thought out interfaces are valuable and worth working on.
"I think a good place to start is through the service design lens — “This is Service Design Thinking/Doing,” Jon Kolko’s books, and IDEO’s handbook (all available on Amazon). These are all practical, business-oriented works, and will give you the language and tools you need to interact with service designers or conduct your own explorations.
In the tech side, I still think there’s a lot of value in reading through Booch and friends, even if you don’t go on to use UML in a formal way, and even old, deprecated approaches to modeling (think Shlaer-Mellor) can be valuable as an introduction to more modern (and generally more complicated) approaches to domain modeling.
For more academic and less practical works, look into philosophers of language and communication (Rorty, Habermas, Latour, etc) — if you don’t have a background there, try the “Very Short Introduction” series, which gives a good grounding before moving into further secondary or primary literature. What you get from these writers is a way to think about language, communications, and complex systems, so you’re building a conceptual toolkit. (Analytic philosophy, which is another, “mainstream” branch of philosophy, also has applications in the formal comp-sci route.)
Sociology and STS (Science and Technology Studies) kind of occupies a practical middle-ground between philosophy and design; I’ve often said that when we do service design, we’re really just doing folk sociology. There’s no shortage of textbooks, but I’d recommend looking into academic field guides for ethnographic studies (a fancy term for “going out and talking to people about what they do”) and ethnomethodological studies (a fancy term for “going out and talking to people about what they believe”). While the service design books will give you practical tools for conducting workshops and research programs, sociology books will give you a firm grounding on why those tools are used, and how to theorize effectively on what you uncover. I also like measurement theory, which is a branch of psychology concerned with understanding how to define measures and metrics — great tools for clearly defining data and reporting.
Finally, in addition to all this, consider digging into a corporate finance text or two; the three legs of the stool are product and service design; technical and operational architecture; and finance and sales. If you can read your company’s balance sheet and cash flow statements, you’ll be in a much better position to understand where you need to target investments, what the likely return on investment will be, and how much cash (or debt) is available to make things happen. Start building up a good knowledge base in all those areas, and you’ll have the tools necessary to know the “where, how, and why” to apply the “what” of technology to your organization’s problems. "
Especially since reading is great, but actively engaging with what you have just rea, d by trying to solve some related small scale problem, is even better !
I knew a developer who said of such people “they spent six months in the lab to avoid two weeks in the library.”
I think at least to some degree people sort themselves into projects. Typically, the better ones that are also happy and capable with reading other people's code command ability to place themselves into projects that seem like more intellectually rewarding -- which means they are much more likely to form the original team for a new project than to join a project that is already legacy and is not meant to be rewritten.
What strikes me is that it actually is easier to write new code than to properly maintain/refactor old, foreign code. Working with large amount of old code requires a lot of hard, real planning and decisionmaking and learning to live with and adjust to decisions made by others in the past.
This hits "maintenance teams" twice -- not only they face actually more difficult task but they are also less likely to get good devs to help. It does not help that management is usually less likely to give resources to teams in maintenance mode than to teams that are developing new toys.
Right on the money. Everyone loves greenfield because you get to build things up from scratch, try out your own ideas, and there's this feeling of tremendous velocity. You feel like you're just tearing through code, kicking butt, delivering wins. Then the difficult requests start to come in, which usually means feedback from real human users. That's when the rubber hits the road and the grumbling begins.
And like you said, once the project is seen as maintenance it is 'not sexy' despite the fact that all successful software definitionally makes the most money while in the so-called maintenance stage.
Well, that sounds like an utter waste of time. I just wanted to know if this endpoint responded to a GET or to a POST request.
EDIT: to clarify, when I say “problems with HTTP” I mean actually understanding the protocol. Many people for example are unaware of the power of the Cache-Control header and all the things it can do. I used to ask people in interviews what the Content-Type header is for and enough senior engineers didn’t know that I simply stopped asking not to make them feel bad. It’s truly shocking how many people will go decades operating on muscle memory, never bothering to understand what’s actually happening when their code runs.
Thankfully basically everything is utf8 these days.
But I don't think I've read any books on that. What I do is read the code!
I think can do this mostly because I'm interested in it. SW projects die because the code isn't maintained properly and becomes unworkable. So I work on being the counterforce to that, keeping the project alive.
Most engineers are into writing new shiny code, and hate "cleaning up other's mistakes". That's also often rewarded. The results are well known :)
You’re right that there are very few books available showing people how to do this. Usually “Working Effectively with Legacy Code” is recommended but it’s just one book and so can’t cover everything. It’s an area where on the job training and experience definitely makes a difference.
Rather than follow suit, he went in the opposite direction and did what no one else wanted to do… he started a septic tank cleaning company. He said that it was hard work, but lucrative… and that after a while, he woke up one morning and realized that he was passionate about other people’s shit.
AI and crypto are very cool right now. They'll change the world and make some people billions.
But I bet you'll on average have a better career working on sewage plant management software :)
https://news.ycombinator.com/item?id=30651273
They're not about any technology per se, but mostly about exercises in how to tackle complex systems.
(I recently looked into getting a copy of a certain specification printed/bound, but found that the cost would be a lot higher than I cared to pay)
Or, if you’re into arts and crafts, you can get good home binding machines that are not very expensive. It’s actually pretty fun IMHO to punch and bind the paper but YMMV. :)
But I must admit as I get older, I don’t have as much time or patience to devote to this and I’m getting to terms with getting good enough to make a living rather than really understand $new_technology. I think this is partly from getting old and not wanting to sink so much of my own time and mental energy on computer stuff, but also from realizing how little it matters to most PHBs/middle managers who basically hold your fate in their hands. I’ve become much more cynical after a decade in the industry and I’ve realized how much of my own energy and health I have wasted in trying to get a deeper understanding of programming concepts. It was personally satisfying for a while, but it doesn’t pay off for most people. Most orgs want it done fast and cheap, they don’t want squeaky wheels, PHBs think in quarters and only want the thing to not blow up until after they get promoted in a year or two.
Developers who deliver that kind of low cost code fast (albeit low quality) have much more stable careers, better promotions, and get bonuses.
The rest of us ruin our mental health, paint targets on our backs, and don’t get as far. But man do we know our shit.
What I've found is that good companies and outfits do exist out there, but usually they can't pay as well. Or they are boutique consultancies where everybody really cares about the craft...but then you have the realities of consulting to deal with (i.e., dealing with really difficult people).
I am not sure what to say other than: please don't give up hope. Things can get better but you may have to change your expectations or value system to find a place that works for you. Or if you have the stomach for it, try to build a product on your own leveraging your expertise.
For myself, a difficult but necessary change has been not wrapping so much of my self-identity up in 'being a great programmer.' Yes, I know some things in tech, and that's cool, but there is more to life than technology. But just to re-emphasize: I hear you, and it's tough. Wish you the best.
I agree it’s sad but I also have to look out for myself. There is no point in exhausting your energy trying to fix something where no one wants it fixed at best or is actively fighting you to fix it. It’s not even your money that’s being set on fire then, but it is your sanity.
Curious if you’ve found either a better company, or created your own product? It’s difficult to come to terms with mediocrity and needless toil, I went into this industry because I like well designed and efficient systems.
I agree with most of what you said, but this seems a bit ironic.
Your suggestions almost exclusively involve large investments of time with little established proof that they are efficient. Do you genuinely believe reading RFC 2616 "cover to cover" is an efficient way of solving the specific problems they came across?
I would wager most developers wishing to be "really good" actually have a concrete desire like a greater paycheque or employer in mind. If that is true, I doubt reading someone's booklist necessarily is their fastest pathway, and their economic perspective is exactly what stops them doing so.
I think you missed the spirit of my writeup.
But over time this person will be getting better and better and better and at some point they will be able to do their job on the fraction of their time while still keeping momentum and learning more.
This has been my experience.
I have been spending 3/4 of my working hours learning new things for the past about 17 years after I realised this. The actual work takes a very tiny part of my day.
Software development really is a knowledge sport. Writing code is actually pretty small part of the task -- knowing what to write is the important part. If you are able to "write down the answer" so to speak, without spending much time getting there, you can be 10 or more times productive than everybody else, easily.
This. This is how I felt trying to understand legacy core code. I kind of like that though. The idea of being able to improve/rewrite a decrepit piece of code excite me. But you won't be productive, and getting a (somewhat) complete and precise understanding of what it does and doesn't, take a lot of extra time.
But.. that is a dead-end job. There is no career path being the maintainer of a large legacy code-base. It was pretty good if you wanted to rest-and-vest, I could do whatever I wanted. But there were no great features, no demo's, no selling of the work. Even small changes were painful.
Moved to a different team where everything is new and building from scratch and suddenly opportunities open up.
- Was the original team slowly replaced due to cost cutting measures and thusly the people who joined aren't compensated well enough to dive super deep into the system to understand it?
At some point people need to be compensated to care is my theory. Engineers who inherit large systems are either scared or simply don't care, possibly a mixture. But compensate them enough in both salary, time, freedom to understand the code which is clearly critical in maintaining the long term viability of the system.
Thoughts?
If I feel underpaid, I'll do the strict minimum to not get fired and spend my energy looking for another job.
If I feel underpaid, I will do my normal work as best as I can and start looking for another job.
I always try to leave on best terms. You really don't risk anything, you actually improve your possibility of fixing the problem without leaving. And in my case it led to at least couple more opportunities.
Not to mention the world seems small enough and on many occasions I joined the company only to learn I will be working in vicinity of somebody I worked with in the past.
As a fresh developer out of college I had the mindset that frameworks were something you use and they always work and all you need is the documentation. My first job had a lot of "core" code written by another team in another office we would use with exactly 0 documentation. I was trying to get our UI to do something specific but couldn't figure out how to do it by just looking at the functions available, so I ask my manager and he immediately knows what to do, makes 2 small changes (in my defense it was pretty hacky how it was set up imo) and I get what I want.
I remember being moderately pissed because I was already feeling the heat of asking my manager too many simple questions and I just apparently asked another one, so I say something along the lines of "How the hell would I ever have known how to do that, there are no docs available to say that is possible and that is not obvious at all."
He just looks at me and says "You read the core code" and I remember having my mind blown at missing something so obvious for so long. After that I voraciously read almost all "framework" code I could get my hands on, I asked more intelligent questions and my relationship improved my manager and eventually came to be considered a generally good developer.
Once I had this breakthrough I was seeing it in younger developers all the time, and now any time someone asks for general advice on becoming a better developer one of the things I say is "read a lot of code", and more specifically "read the code in your frameworks"
and
> Team stability is vital for software development. Each team needs to be composed of a majority of first-generation developers.
How does this interact with individual or career growth? I don't want to work for the manager who insists I stay on the first few projects I worked on at the company forever...
To take this seriously, internal churn (even if just someone being promoted into management, or specializing on expertise in a small part of the original project, or switching projects when they get bored) is almost as costly as external churn. Five, ten years later how much is their domain knowledge still that relevant to the people working on the code day to day today? It could be harmful if they've been out of the loop and make outdated suggestions, even!
Assuming and desiring your employees will stay in the exact same role indefinitely seems like a huge cause of churn.
If you try to fix the theory knowledge/documentation/practice problems purely through insisting the "first gen" stays where they are, they're going to leave you for more interesting work, and then you're in exactly the situation you wanted to avoid. The only way out is "more effective practice of theory and knowledge transfer" but all the author seems to have to say about that is "This is a team management issue that can only be addressed by a manager who understands the fundamental nature of software development. There is no magic development process that will prevent these problems. It takes skill."
E.g. that's the real hard part yet they completely punt on trying to dig into it.
A big component of what this article is saying is basically that knowledge transfer in the way you're describing doesn't work, and if you dig down to the theory this article is based on, new devs adapt and create their own theory and solutions was specifically called out as a problem. In case 1 of this paper, it's described as second generation programmers submitting patches to the first generation programmers that don't make use of the structure of the program, and add things that are trivially done by the existing systems. Then, a third, disconnected generation of programmers takes over the maintenance from the second generation, and they've added so much cruft to the system that the original powerful architecture can still be seen, but isn't in use because of all the additions.
Knowledge transfer doesn't work at all :) It's not a thing that's possible. The best we can do is for someone with knowledge to attempt to explain it, hope they do a good and complete job of it, and then hope that the explainee is able to internalise it and come to the same/similar conceptual understanding that the explainer had in their mind.
It doesn't tend to be done very well at corporations. It's a skill that good teachers have developed after much practice, which isn't something we tend to hire for.
And it requires favourable conditions, and one of those conditions is not "it's my last day tomorrow and the boss asked me to bring you up to speed on what I've been doing these last six months. You only had 30 minutes free in your calendar, so... let's go"
Is that an improvement?
(I'd also argue that I've worked on systems where I've developed a better mental model of the problem than the first-gen people I inherited it from, because I as a maintainer was under less pressure to get anything that worked out the door initially.)
EDIT: and looking at the paper you linked there, https://pages.cs.wisc.edu/~remzi/Naur.pdf , I'm in complete agreement with "the designer's job is not to pass along "the design" but to pass along "the theories" driving the design" - it was always easier as that maintainer to take over a project where people could tell my why not just what.
A lot of the time they don’t want to understand the system. Maintaining some old legacy system is no fun. Programmers want to do new things their own way. And if there’s no one around to guide them then they will.
This is a management and cultural problem. Enterprise software resembles failed open source projects - no one wants to work on them so they die, get forked, or re-written and replaced.
Contrast this with successful open source projects. A vibrant project will integrate new developers into the culture (or show them the door), and conversely developers who want to change things will have to prove themselves.
Personally, I don’t our ability to learn is that bad. Given time to come up to speed and motivation to read the code and understand it, and some good (consistent) coaching from people who fully understand the code, I believe new developers can be integrated into a complex codebase that they haven’t written.
But this really draws a stark relief from the way software engineering on existing code is treated by every manager I’ve ever met, which is likened to reading a book once in a language you already know. One read through is enough to grok it. If we land somewhere closer to this source material on the spectrum I think that’s a win.
I also look at my own thoughts on existing code. I’ve had the strong urge to refactor out old code I didn’t like… and that is interesting to me. Did I not like the style or just not understand it and want to make my own mental model my way?
The idea that we’re just replacing engineers and they are just re-writing the same code with the same outcomes because we think of them as Lego blocks is interesting as well.
By documenting your work and/or mentoring a junior employee to pass on your tribal knowledge.
Reasonable amounts of churn are manageable if you do a good job of acculturating new people into the team. This has actually been one of my concerns about the switchover to WFH. I know a lot of that acculturation used to happen by osmosis and people working next to each other. I think my team doesn't do as good of a job maintaining it when we're all working separately. We're a lot more efficient now, but I think we haven't figured out a good way to share and transmit context like we used to and I worry what happens if we hit a period of flux in our company. I worry our team culture is a little more fragile now.
I used to think this too, but I think we need to be cautious of magical mechanisms for knowledge transference. I think the real mechanisms are code reviews, discussions of issues+code+project, consults / shared problem solving, design reviews. Just sitting next to someone doesn't work, there needs to be a shared task or problem for knowledge transfer to happen.
I think all of these shared tasks happen with remote workers just as effectively, as long as you have good team comm hygiene.
Talking to your teammate who sits nearby in person is both lower friction and higher bandwidth than sending them a chat method.
"Adhoc conversations" don't magically lead to knowledge transfer, the probability of any adhoc convo being long-term-meaningful is low, but they greatly increase the cumulative probability.
To me this is the a huge incentive for building strong teams. Nobody should be indispensable; it's a disservice both to the company and the person. Pair programming, collective code ownership, intentional cross-training, good automated tests, continuous deployment, and good automated monitoring have all been very helpful to me there. And vacations! Everybody should take long vacations where they are out of contact. By managerial force if necessary.
Decision makers for American companies tend to use "Time spent at work" as a proxy for "Productivity". That used to work just fine when the job was pulling levers on an assembly line but turns out to not make sense when you are talking about software development.
Do you actually think of yourself as a capitalist though?
Alternatively: just promote and pay your people as they develop, even if they're in the "same role".
What I learned from that project is that being indispensable sucks. That lesson has recurred in my career either for me, or when I got wiser, for other people.
It's not enough to write reasonable code that 'works' or document it as it is now, there's a whole unmeasured and thus under-encouraged aspect around maintaining a legible edit history. I often am trying to get into the head of the bug author to figure out why they thought this was a feature in order to preserve functionality while fixing bugs. They aren't always around, and they aren't always fun to talk to. If you have the history, the person is free to work on other things. To gift them to people they mentor, or otherwise re-home them. If you don't have that, if it's all in their head, they have to leave to get away from the project.
If I can figure out what you're doing I can figure out how to change one or two conditional blocks to fix a problem. Your code stays intact. If there's no history anyway, and the code is sketchy, I'm far more likely to take the code away from you by rewriting it. We can't have black boxes on the team. That's a ball-hog, and ball-hogs get benched.
If you don't show your work, then you're going to find you have fewer and fewer interesting responsibilities. Because "how we got here" is also a "why" and we ask Why all the time.
I try to have them clean it up but often the best thing I can get is a squash.
It's simple, makes reviewing easier and works for 90% of the cases.
Maybe I'm missing something, but I find it painful to rebase into a neat history, and my colleagues would probably hate me to police them around to do it as well.
So it becomes a fight between the two.
Grow a garden, not a tree. Let's disambiguate the word "project" from the word "system" (not "product" -- that's sales). Let's call a project a time- and scope- limited effort to create or modify some system(s) in your suite (your garden). I think it's a common mistake to mix these two up. If projects just drag on forever, then yes, employees can feel stuck. By this definition, I certainly would not want to work on one project forever.
What about working on one system forever? The problem there is that to stay busy, you're forcing attention on a system that might not need attention. At the end of a project, a system should be fulfilling its purpose, and it should do so indefinitely, forever. That purpose may expire, but the system shouldn't. There should be no urgency to keep messing with a working system.
So let's work in one garden forever. Systems will grow; we'll prune and re-plant them; we'll give the healthy ones the barest maintenance it needs; we'll give the dying ones the sweet release of death; we'll shape and braid them together. All your institutional knowledge about how System A works will be great when you're writing System F that depends on System A, or when we're splitting System A into Systems A1 and A2. I would absolutely love to work at a company where I can work in one garden for the rest of my life. That's my dream job.
Sidebar: do not make the mistake of assuming "system" means "microservice"! That's only one of many ways to define system boundaries.
If you can grow people into being the ones who know how the components of the system work together at a layer above how the code for any given component works, you can have a retention plan and keep institutional knowledge. (Or even then move people into a layer above that, as the system grows, etc).
You still won't retain everyone, some people want to jump between gardens, but you will be able to retain a LOT of people, and it's much better than telling people they have to own all their individual lines of code forever.
Wonderfully stated! Losing sight of this is I think where projects start to run into trouble with scope-creep and the ”fix” vs “enhancement” issue. It also shifts the perspective of what is a satisfactory state for a finished project vis-a-vis maintenance plans, dependencies on aging systems, etc. Your garden metaphor is also beautiful and IME accurate.
The first project is maybe a bad example but if we say the "nth" project instead then:
- not always changing members doesn't mean never changing, companies should just not want you to jump between projects all the time (except maybe as junior programmer) and you shouldn't want this either as it's hard to build up proper deep expertise this way (instead of a lot of shallow expertise)
- if you are still in the same company and the company is healthy then it should be fine if you get asked questions about your previous project for some time after you switched (through not arbitrary, just as an "emergency" thing)
The problem is that the concept of carrier and seniority is seriously messed up in the software industry. Often shallow knowledge about a lot of things is more valued then actual expertise in the things needed. Similar you will find a surprisingly lot of people believing that a senior engineer which doesn't try to get into team lead/people managing positions is doing so because they "surly are incompetent and shouldn't be hired" (IMHO completely rediculus but I have still seen it often enough, just most times in a less directly phrased way). But in the end senior software development skills and team lead skills are distinct skill sets. Implicitly pressuring someone who is good at solving technical problems into a team lead position (or the other way around) is just a pretty stupid decision from a human resource utilization POV. (This depends a bit on the definition of team lead and yes team leads also need to have technical skills but most important they need to have the skill to listen to their team and not get stuck on their opinions, etc.).
When TSHTF and we need some expertise from the original builders or operators of a system, it's a lot nicer to have them on another team than at another company. If they still work for us, somebody can make the case to them (or their boss) that borrowing them for a few hours to fix it is in everyone's interest. A lot of times they are in some kind of management position now and they kind of smile and say "Wow, I haven't thought about that system in a long time. Sure, I can take a look." and it's even kind of a fun diversion from whatever budget meeting they were in.
If they work for somebody else, the answer is often "sorry, I'm busy" or just a hangup when you explain who you are and why you are calling.
This part rings very true, as someone who's been through plenty of rewrites / "make module X not suck" projects. A side observation here is that usually the original author (sometimes long-departed or departing) understands very well the limitations/flaws of the original system/implementation, but can be excluded from the design of the replacement for any number of misguided reasons. Trying not to bruise the ego of an early employee, thinking that they wouldn't agree with any changes, whereas those are often the people in the best position to guide a rewrite, and often would love the chance to fix some of the decisions they had to make originally.
Fine, so long as your rewrite is not checked in. Otherwise, this approach will surely lead to chaos when prior developers who figured it out now have to re-learn your version, resulting in the exact problem this article is addressing.
Rewriting and submitting code mainly because one does not understand the original version is usually not a good idea; understanding the original code should typically be a prerequisite to replacing it with other code. Otherwise, how would the developer know that the new code was better?
I relate to this so much. I'd offer an open letter to some poor anonymous soul who inherits one of my dumpster fires. Because I'll certainly never be given the chance to revisit anything.
Yep, my shit's broken. It was written long ago, under different constraints, for a different audience. I made the best trade-offs I could. I documented them! Though you'll probably never find my writings, and if you do trip over them, you won't understand them yet. Today, I'd scrap most of it if given the opportunity. But to whomever is now taking on the rewrite, you know that little nugget at the core which perplexes you so? The one that doesn't fit your mental model of any type of programming you've touched in your career? That there is the core value proposition that makes it all possible, and if you throw that out, you've doomed yourself to learning my wisdom the hard way. If you're at all up to the task, then the next few years of your life and the remainder of your days at this company will be spent fixing the same problems by reading the same papers and obtaining the same obscure domain knowledge. No, it can't be abstracted away. Yes, it's fundamental. No, no one else will really understand it either when you try to explain it, because no one else without the accountability you now have will sink enough of their time into building the mental model you've had to construct. Good luck.
My own efforts don't quite earn me "the big bucks" that I read about at companies I'll actually never work for. Just a respectable east coast income that I shouldn't complain too much about. Don't know that I'd be happier anywhere else anyway. The bills get paid while I'm making the world a better place.
On a bad day, I'd say we do it because we're suckers for punishment. It's in our nature.
Not sure how true that is, or if it’s more myth. Also not sure how common it is to have original devs participate in the re-write, or if this is more something they do if the original devs have moved on. If any Googlers on HN could chime in, I’d certainly find it interesting!
The code worked, and continued to work, and didn't need really active development, so the org decided the project's programmers should move on to something new, or that the knowledge they have isn't especially valuable, and the programmers aren't worth keeping happy.
And a couple of years later, no one knows how to update the code for a changing outside environment, updated input styles or whatever. And all that investment is gone.
This includes things like airline reservation systems, scientific computing models, compilers for obscure dsls, and the like.
Companies doing rewrites every three years have a hard ceiling.
That's a blind jump. We don't know the purpose, we don't know how much was invested, how much was already recovered, and how much more could be earned if the program was allowed to live.
All we know is that it has recently hit fitness to purpose, and that the fitness caused it to be abandoned.
I have and still am. We have stuck one ancient piece of software in a docker container and have our fingers crossed that it continues to work. This software is OLD, early Perl 5 old. Nobody in the department understands Perl or wants to. The business (read: product owner) has deemed it unimportant to update or understand, but I assure you that if it stopped working, it would affect millions of tax payers, upper management would find out (read: VP, CEO), and heads would roll.
Everything is fine, until it isn't.
Your thinking from the perspective of a startup might be true, but from the perspective of a company that is many decades old and has a dozen layers of management... perhaps not so much
You have been able to put it in a container and it’s running, so unless the kernel itself changes drastically so old libc will just stop working, it’s going to be able to work unchanged for a decade more.
But the communications problem between management layers is way bigger than that, and could probably bite in some other situation.
I am always faced with this issue with non-software companies in industries where I've worked such as nuclear, defense, energy, and healthcare. At these places, I've confronted "hands-off" modules that are effectively black boxes of complex logic that has been vetted over decades, is written in C or Fortran, and nobody is allowed to modify.
I can't imagine that situation existing in a software company.
Eng management needs to convey that this is an Achilles' heel that is important to understand & be able to update.
No engs want to work on perl5? Bribe existing employees, hire someone who wants to work on perl5, or a darker third option.
The product owner can even spin this as them doing a good thing to future-proof the business, even if it means their team loses velocity on other projects.
And that is the problem.
I don't know how bad that code is - a lot of Perl code from that period is not good - but learning enough Perl to have a shot at it literally should take a competent developer a few minutes if you had someone explain it to you.
You might find the language weird. $ means "the". @ means "these". % indicates you're building a lookup. Which you then access with braces. So %foo is a lookup. And $foo{$bar} should be read "the foo of the bar...".
Early Perl 5 means it probably doesn't use references or objects. You do use regular expressions. While regular expressions are built into Perl in a way that makes them easy to use, Perl-like regular expressions are in pretty much every language. So the effort you need to use them is directly portable to Python, JavaScript, Java, etc, etc, etc.
Fluency takes a lot longer. But getting to the point of "I can puzzle my way through a language I don't know well" should be doable very quickly. And it is a skill that every developer should build. Because I guarantee that whatever language and toolset you are using now, you'll need to be able to switch at some point in your career. And practicing in advance will help you. Plus being the person who can puzzle out old ganky code in another language lets you be a hero in fun ways!
No, that's not the problem.
> the business (read: product owner) has deemed it unimportant to update or understand,
That's the problem. It's not on a random person in the department to spend their own time trying to solve a problem the business doesn't want invest time or money into solving. I don't see you offering to do this for free, so don't claim that's a problem for others.
All of the talk about how old and bad the language is, and how nobody wants to learn it is tech speak for, "We can't support it as is, we need a rewrite!" Anyone who has been near tech for a long time knows "rewrite" means "tech is going to break it for unclear reasons and uncertain gain, and in a few years will want to rewrite it again. Tech people always underestimate the cost and risk of a rewrite, and also overestimate the benefits.
Business is not being stupid in saying that tech can't rewrite the software. The software is there, works, and is being relied on. Tech should figure out how to support it without a risky rewrite. Don't stand on, "We don't want to learn Perl." Learn how to be a polyglot. At least when it comes to reading. And to the extent that tech understands the value of being prepared, tech should prepare in advance. And figuring out how to read the code is most of that battle.
Now you accuse me of not offering to do this for free. But in fact I offered the best tip I could for a non-Perl programmer who is trying to puzzle their way through an early Perl 5 script. Given that I'm an internationally recognized expert in Perl, that tip probably does more good than you realize. Learning how to navigate legacy code in a legacy language is a learnable skill. And you learn it by trying, then finding you can. And no matter how much you don't wish to learn any particular legacy language, having the confidence to know you can is valuable.
No, it really isn't. I'm happy to learn a new language. The reason is correctly stated by a sibling post here (WesolyKubeczek).
That we've been able to containerise it has limited a lot of the risk, and any upstream issues we have going forward we should be able to write some middleware to sit between it and transform the data into the format we need. We can extend the container to proxy any requests from the ancient application to the new middleware. We have a strategy, the product owner is happy with that, but we've outlined that maybe the solution won't work as we think, there are always unknown unknowns...
The issue is that if a problem does arise, it will be immediate and the effects will be there for as long as it takes to get a fix up and running, which could be weeks to who knows how long. That's a real risk in my eyes, but it wasn't to our product owner.
It will affect a lot of people, it's a pretty widely used service! It's entertainment, so people won't die if it fails.
The risk was deemed low enough by the product owner that we can just leave it in a container until it's finally sunsetted in 5-10 years (or so...). We've had 2 product owners since the OG product owner made that decision, so it could just be the product owner who made the decision thought they'd be long gone by the time it became an issue.
You're making assumptions about people you've never met, you don't know me and you don't know what motivates me.
You aren't as good at "reading between the lines" as you think you are.
---
From your profile: My main programming language is Perl
Oh, it all makes sense, you're hurt that I called Perl 5 old! That's funny. I've never written production Perl, I am not making a judgement on the language.
Yeah, but the motivation to learn Perl 5 as your next language is about as wise as learning COBOL as your next language.
That's probably why parent is finding it hard to get his developers to work on it - lots of effort for negative gain[1]. Anyone wanting to learn Perl 5 in 2023, for no additional money, is probably legitimately crazy.
I don't understand why you don't see the problem that parent has - no one wants to learn Perl 5 because it is a dead-end, and takes up energy that would otherwise be spent moving your career forward.
[1] The opportunity cost is immense - the time used to learn Perl 5 enough to maintain the legacy code is time that could have been used to learn a current language that results in a payoff.
Heh, it never fails to crack me up how relative old is. I’ve worked on software that predates Perl, and even UNIX itself…
Sure I have. And the first few times I too thought that it mattered. But it doesn't. None of the stuff we care about as software developers actually matters. We are just pawns for the people making money decisions. Completely replaceable automatons. If what you are doing is not directly supporting a strategic business goal, you are doing nothing but costing a business money. And cost centers are the first to be cut. You can sit there with your sense of self importance, thinking that the cleanliness and elegance of your code matters. But it just doesn't. Nobody cares. And the sooner you let go of thinking it matters the sooner you can focus your effort on what does.
The reality is that when whatever old house of cards you're running fails for whatever reason, the business will switch to a vendor that covers 95% of the use case, while being much cheaper and not requiring FTE support. It will be a tiny bump in the road for the business, replacing this thing that you thought was a world beating make or break technology.
I used to work for a company that had an internal tool. It wasn't used anymore, everyone that worked on it was long gone, the source code was "lost".
There was some emergency and someone needed to use the tool to look at some old data. Lots of panic and people claiming it was impossible.
I opened up the executable in ollydbg, patched the executable so it worked properly, and life went on.
I'm sure the source code wasn't lost, it would have been on a tape backup that people were too lazy to try and recover.
If the source code is available then it's even significantly easier. I don't buy this whole "nobody knows how to update the code" reasoning.
If the code can't be updated, it's a matter of incentives and prioritization.
In my specific case, the tool had a whole bunch of hardcoded values. It checked authorization against an LDAP server, that all changed. Connected to a sql sever that moved + credentials changed. Assumed certain files existed on certain network drives which were moved.
Technically, it could have been solved by faking all the infrastructure. It also could have been solved by digging out the source code from tape drives. But everyone was running around saying it was "impossible". I was shocked how quickly people tossed out "impossible".
It could have been done with a simple hex editor, but the new values were longer than the old values, so the strings didn't fit. I put the new strings at the end of the .data section and then just changed a bunch of offsets.
I agree that if you're writing the next version of the software, you can't do that quickly when nobody knows how the current version works. But chances are, you're not writing the next version of the software quickly even without churn.
Me too. At best, they would need the previous guys only in one condition: very short time constraint (have to figure it out "today"). Otherwise, it's not hard to figure out at all, even codes from a complete idiot. If he can code the program that worked, the code is very likely be figured out by experienced people very soon.
Sure, you can figure out how to change it to do some very specific thing that somebody identifies as needed. What you probably can't do (at least not in any reasonable amount of time) is figure out all the things you're going to break in making this change. If they followed good software development practices, left a good comprehensive set of unit tests and documentation then you could do that - but they didn't.
> even codes from a complete idiot
If I was wrong, there wouldn't be anything wrong with global variables or goto statements - any code that "works" would be isomorphic to any other code that "works".
That's how it works even with the same guys on projects. If you think it would break (no tests found), write the tests first. I don't believe someone would write a complete mess million line of codes that works without some tests and docs, that would make him kinda genius.
And global vars and goto is not hard to figure out, it's hairy for sure.
Then you can start by writing the tests. That forces you to dig until you actually understand what's going on.
My "trick" is to write black box test without understanding the code. Like this:
1. Send in some data 2. Observe what data comes out. 3. Make a test asserting that! 4. Go back to 1.
That makes you a great engineer that saved the day. Not all companies have great engineers that can save the day, or keep them, or even identify them.
Sometimes you also need to add features, which can be quite a task if you don't have the source code.
Actually, in most cases - no, they can't. Or, rather, they can't figure it out as fast as they're "needed" to.
> if there's a real need with no alternative
What happens instead is that somebody with no real knowledge of software development says, "we need this change made in two weeks. We need this." That this might be impossible for any human being, living or dead, is completely ignored. If the programmer this is dumped on even suggests making any clarifying improvements to the code, even that is rejected because "we need" this.
And two weeks come and go, something was changed because there was a deadline, but the something that was changed broke a dozen other things nobody even remembers were there, and now there are bugs waiting to be discovered by unsuspecting users.
> it's a matter of incentives and prioritization
I know what you mean - it is, technically possible but it's never feasible because it's not predictable.
I've forensically self-onboarded a few times and yeah, it's at least 12x more expensive than well written docs + pairing.
It's economic for some projects but it's still crazy to do it if it's at all avoidable.
I see a lot of the time programmers doing the easy/fun thing rather than the hard beneficial to the business thing.
Loyalty gets you very little in today's climate. Why should I spend time understanding the current system that was developed on yesterdays hotness?
I need to spend my time here developing on the latest trends on a new solution (learning more from running into the same walls the current system did) - not learning the domain knowledge + old framework, so I can move on after a year and earn more at the next place.
Is this good for the world? No. But unless you want to swing the hammock and work at institions that value incumbents, the incentive is to do whatever work improves your resume, not what benefits the company.
I don't personally like it, and I often see old systems that are just fine get thrown out and replaced with the a less featured replacement that grow into the same kludge as the first, but I can't ignore that when you do that you learn way more and have better examples to point at in interviews.
"I replaced an unmaintained, buggy legacy tool with a new version with X new features" >> "I maintained a legacy tool"
Simple. Because that is your job: to solve problems for the company using the current technology that the company has the resources to handle.
Also, the newest hotness is not as hot as you probably think.
Other coworkers had interacted with the system a little, but not extensively. It was a bunch of C for (rock & ore) mining software. It was intimidating and it took time, but she cracked it, and then happily (& justifiably) bragged that she added features to code that had been written before she was born!
In my own experience, devs aren't often overworked and do have the time to do stuff like this. They just rather not spend the time on it for various reasons.
What incentives would get you to work on an old legacy project?
Well, by that logic, someone can build it from scratch.
As an anecdote, your example demonstrates the truth of the reasoning. Absent extraordinary or unorthodox effort, such as patching the executable manually, programs do suffer from loss of understanding. Yes, you made it work, but can you really claim to understand the program and how it was written?
Of course, given enough time and effort, most code can be reverse engineered to a level of understanding good enough to work on it. In practice, most organizations are going to decide it's better to spend time and money on a replacement.
> I'm sure the source code wasn't lost, it would have been on a tape backup that people were too lazy to try and recover.
That's certainly one, rather uncharitable, way to look at it. But what constitutes sufficient effort, given time and money constraints in the real world? Unless that backup literally contained the only copy of information essential to the continued existence of the company, there's a limit. And what if they did find the backup, but it was on a magnetic medium that had degraded to unreadability?
I've made my career working on legacy code. There are definitely ways to understand systems even when nobody who originally worked on them is around, but business constraints have always determined whether or not the effort proceeds or is abandoned.
Maybe not, actually. Assuming that the code was written in the 90s, there probably wasn't any version control (only backups at specific milestones).
With no version control, there's a 50:50 chance that the source code you found for some deployed version is not the same as the code that was backed up.
Sure, I had source that I had written. In source control even, was using fossil. But I thought my binary might end up somewhere decoupled from that source, with some other engineer tasked to modify it, and I hope they find the dump source option listed under -help useful.
I've done simple binary patching, and thank my lucky stars that:
1. The replacement configuration value strings were shorter than the original
2. It was written in C, using nul-terminated C strings
If this was almost any other language, I would have more work even if the replacement string was shorter, because I'd have to fix a bunch of offsets everywhere.
Software product is delivering value, maintenance isn't needed much, people move on to other projects[0], people forget about it, then a couple of years later a change is needed.
But how would the first response in that "couple of years later" scenario be "throw away all that investment and start from scratch" instead of "dig into how it works"? That's not a long time, really.
[0] this is a good thing, IMO. If every project needs constant maintenance from its "owner" you as a company are going to rapidly run out of resources to do new things.
(There's a closely related thing, here, though, that as an individual or a low-to-medium level manager, keeping track of these risks and low-level maintenance areas is part of your job to find ways to keep it from ever turning into a disaster. At some point - 10 years, 15, 20... maybe the knowledge gap gets way too big for the current team - then get proactive.)
Because start from scratch is more predictable ("estimable") than "dig into how it works". "Dig into how it works" is better in the long run, but will take longer. All software management is laser focused on speed and predictability rather than long-term quality.
Replace-from-scratch has all the same risk of getting things wrong because of that lack of knowledge and a bunch more new-code-writing on top of it. Estimating new development often fails; estimating it in the face of business requirement uncertainty is even worse.
To certain styles of management focused on "always building new products" to guarantee their worth/value, they absolutely will get blinders on and believe "new products are easy and more predictable than maintenance of old products". Those blinders are hard to take off once they get into that sort of pattern. All of their processes are built around "new products". That starts to be the hammer that wants to dismantle old products because they don't do maintenance (that's someone else's problem) but they surely know how to build new products. They are the build new products group, that's what believe they do. Building new products is exciting and they can be passionate about it.
I also think that's generally the wrong way to work and does plenty of harm and creates a lot of unnecessary stress on development teams caught in those cycles. But I've certainly seen plenty of management teams that do work that way in the real world and maybe don't question it enough.
I think it's a mistake to make the "fix the old" or "rewrite from scratch" decision up front from a position of high uncertainty about the old system.
I'd go with a strategy of starting a timeboxed exploration of the old system with the objective of understanding it better, hopefully well enough to have a reasonable idea of what it will take to fix it. Then make the decision from a better informed position. Yes sometimes the decision may still be to rewrite because you now know that the old system will be excessively hard to change but in many cases you may find out that the old system wasn't as bad as expected.
Learning the old system enough to keep working with it, meanwhile, looks a lot like a couple developers staring at screens for months without results that look impressive to anyone who matters (as far as granting rewards goes) and with very uneven progress. Plus they've gotta be pretty good, experienced developers (so, likely expensive), or the risk of breaking shit as they begin trying to change the system, goes up a ton.
And then they responded with an absence of reality. A two-week deadline that can't be moved? Um, no. Ever hear of reality? You might visit there sometime. It's a bit harsh on people with ridiculous demands, though, so adjust your attitude before you go.
And then, nobody can fix the existing one? You not only have the ridiculous demand, you rule out the only possible way of meeting it? What do you think is going to happen, the elves are going to work all night to magically create a solution?
This sounds like an epic management failure from start to finish.
I worked on an application where I knew just enough to make spot changes, while the other developer who had made the most core changes continued on with the core. Then he left.
So as more core changes needed to be made, none OMG due to Production issues, that made me dip into more and more of the legacy code, and I grokked what was going on. I wasn't happy with it, but I understood it enough to know that to make it truly maintainable would require a total re-do. But the code that was there worked, and as time went on I even took other developers' bolt-on additions and made their functionality take advantage of the sub-optimal idioms the core used, so that at least if someone else had to make changes, they'd only have to deal with one pattern. I've made wiki pages to describe it, but still any other developer who works on it ends up asking question after question and complaining why the documentation isn't in the wiki (it is; they just don't want to look).
We eventually moved to another servlet container, and I filled in the missing functionality the new server lacked, still keeping the original core code.
I've done that with two other applications at our company as well. Why haven't I moved on? Hey, I'm older now, and it's an 8 to 5 job you can't easily find any more.
Naur on Programming as Theory Building (1985) [pdf] - https://news.ycombinator.com/item?id=31500174 - May 2022 (4 comments)
Naur on Programming as Theory Building (1985) [pdf] - https://news.ycombinator.com/item?id=30861573 - March 2022 (3 comments)
Programming as Theory Building (1985) - https://news.ycombinator.com/item?id=23375193 - June 2020 (35 comments)
Programming as Theory Building (1985) [pdf] - https://news.ycombinator.com/item?id=20736145 - Aug 2019 (11 comments)
Peter Naur – Programming as Theory Building (1985) [pdf] - https://news.ycombinator.com/item?id=10833278 - Jan 2016 (15 comments)
Naur’s “Programming as Theory Building” (2011) - https://news.ycombinator.com/item?id=7491661 - March 2014 (14 comments)
Programming as Theory Building (by Naur of BNF) - https://news.ycombinator.com/item?id=121291 - Feb 2008 (2 comments)
Agree with the theory of programming as theory*, but here's the problem with that essay in an era of software-as-a-services and platforms:
A SaaS offering or an Enterprise runs on a stack or assembly of software, each of which has its own Theory.
The problem with no churn is that most of the Theory is not in-house, it's elsewhere, and evolving. If you can't tap into that, you're falling behind. If you don't know it, you can't remix it into your own.
It's just math to convert an org's "refresh rate" of outside-in knowledge (aka churn) into the average lag of that organization behind current best theory and patterns. A 10% churn means an average of 5 years out of date. A 20% churn means at least a fifth of your org is 5+ years out of date.
A couple key antidotes, though (and there are of course more):
Realize that knowledge transfer from (a) outside in, then (b) among, is vital to theory maintenance and evolution.
That means (a) have a great "always be hiring" pipeline of fresh Theory and build processes to make knowledge sharing of outside and inside theory frictionless and routine, and (b) have a great "always be learning" approach to exposing developers not just to the internal theory but to the best and most up to date outside theory.
The better an org is at (b), the less it needs (a). The worse it is at (b), the more it needs (a), but -- crucially -- it must refresh with candidates who know more recent theory than the ones doing the hiring!
Churn is not bad, that word is spin. Internships are a thing, so are externships (firms should do more of this). People, and theories, grow by exposure to novel challenges. Circulation is good for the whole ecosystem of software development because we are building a collective Theory bigger than any one org.
This remixing of ideas is how our collective Theory evolves.
* Most recent decade of my career involved a lot of evangelizing to enterprise management that developers are not project teams to form and disband as fungible resources. Think in terms of capabilities to garden, not projects to remediate.
compare https://news.ycombinator.com/item?id=34315203 (and its wider context)
Even not many people subscribe to Peter Naur's "Programming as theory building"[0]
And, in fairness, that is a reasonable thing to want. The problem is it's not clear whether or not that's possible, and it is clear (but completely ignored by the people who ought to be aware of it) that our current software development methodologies don't even come close to allowing it.
It's not.
Attempting to say it is is very akin to wanting a single, pre-created bridge to be able to span any valley. But valleys come in all shapes and sizes, and so too do the bridges required to span them.
Certainly, there are some parts that are the same. I think most of us complain about cookie cutter CRUDs or byte shuffling, but even in all that, there's generally some higher purpose business reason as to why the software is doing the byte shuffling that I am, or more deeply, why the software is doing the byte shuffling the way it is[2].
And honestly, I think that oversimplifies it, when it comes to the problem and the software that attempts to solve it. The problem can and often does change in software … so it's akin to a valley that shapeshifts, adds new local laws of physics, and requires the bridge to change and adapt. How changes to the software mesh with the existing software, and why that might be — is unique to the project. (And, as Naur argues, contained only in the heads of the developers, and what is in their heads is not even necessarily the same.)
[2] And to extend this with the shifting valley analogy, when new byte shufflings come along, would they benefit from this program's current design? If I need to extend the design, would a given extension work well? Are the requirements different enough that I need to consider something separate for this new requirement? etc.
So I guess it doesn't actually work for plumbing problems either.
Capturing the knowledge is expensive both initially and over time as the software itself evolves. Disseminating it is expensive because it costs O(NM) time, where N is the number of people receiving the information and M is the average amount of time taken to absorb it. Dissemination is so costly, so we tend to do it in a "lazy" manner, meaning non-strict or as-needed.
I think the best software systems I have worked on do a number of things:
1. They define enough ad-hoc proofs or invariants via unit tests. It's possible to work around these by simply disabling the tests, but, assuming your team hasn't devolved to that point, the tests are an excellent source of machine-readable and machine-executable knowledge.
2. The software is designed modularly so that one can operate on a part of the system without having to understand the entire system or worry that an unrelated part of the system is going to break.
3. The system is kept as simple as possible. The less knowledge one needs to accomplish the same task, the better! This is why KISS is so important. It's not for the person writing the code so much as the person debugging it a year later! As a corollary, a simple system tends to have less dependencies. Dependencies are actually costlier than people realize. Every dependency suffers its own bitrot problems and requires knowledge to handle them as part of maintenance.
The problem is, just like how at the start you find lots of devs who don't want to write unit tests, you usually have a lot of devs who resist writing (and reading) docs. They'd rather interrupt someone to ask, or thrash around, or move onto the next task the moment some code is written.
What helps:
1. Keep your docs alongside the source, in version control. This lets you gate code reviews/merges on docs being updated. Someone changed the way the code works but didn't update the docs? Then the PR is rejected until it's fixed.
2. Leading by example. If the TL for the project updates docs with their code changes, and visibly spends time gardening the docs, then they can't be accused of hypocrisy when they demand the same standards of others. Praise quality docs work when done by others.
3. Make the docs beautiful. Let people have pride in their work. It's so easy these days with great static site generators.
4. Answer questions by linking to the docs when possible, not ad hoc Slack/email/ticket discussions. If the docs don't tell someone the answer ask them to hold on for a moment, update the docs quickly and then link to them. Obviously this requires a fast / lightweight turnaround process on review and merge of docs changes, but it can be done.
5. Avoid wikis. Quality requires ownership, someone who is directly responsible and can enforce their will. Wikis don't let you do that so they become a commons, and you end up with a tragedy. Use Markdown or equivalent in a VCS repo so you can refactor docs as they expand, make sure there's someone (e.g. yourself or a trusted lieutenant) who is expected to spend time paying down docs debt every so often.
6. Docs bugs should be a thing. File them. Expect people to fix them.
7. Make it clear that people are expected to read the docs, not just write them. If someone is regularly asking colleagues questions that they could have answered by reading, ensure that's treated like other performance issues i.e. their managers know and the need for improvement is clear to them.
There's more that can be done but these things do work. The biggest problem is always passive resistance. Just like with unit testing or code review, if a culture of avoiding it grows then you'll find it hard to introduce, people will passively resist because it's not as much fun as just banging out more code.
With these approaches the robustness of a program to churn can be improved a lot, you can scale teams up quicker and productivity improves.
I do believe documentation is important, but, having joined recently the largest corporation I've ever been a part of where I work on products of a larger size than ever, I am amazed to find documentation here to be as bad as, or even worse, than anywhere else I've worked, and most of it is simply outdated and irrelevant. I guess I need to work somewhere that does documentation well to believe in it more.
I also consider myself a good technical writer. Unfortunately, it seems that many software developers don't share this skill, and this could be a major reason why so many resist writing it. I think a litmus test for this is whether someone prefers to share information via a document, or via an in-person meeting, a conference call, or a video recording of a meeting. Unfortunately, videos and meetings are not searchable, indexable, updateable, or quick to scan. But they seem to be preferred by the same people who don't want to write anything down.
In prior projects I did it too and success was mixed. On one hand, the docs we got out of it were objectively great, as in, users complimented us on the docs regularly, they were a source of competitive advantage and we could tell they worked because users would regularly show up successfully using advanced features of the product without having asked us any questions.
On the other hand, creating a self-sustaining docs culture was hard. People would do it when faced with the expectation of doing it, and often the resulting docs were fine - tech writing isn't as hard as it's sometimes made out to be - but getting other devs to really buy into it and become docs evangelists themselves was much harder. Also of course you'd have inevitable team friction when someone really hated writing stuff and would have to be forced to do it, and then they'd submit min-viable or low quality work. No different to having a team member who refuses to write tests, of course.
I think this can be solved with time, with cultural change in the industry. One problem, and please don't take this personally because I'm talking really generally here, is the view that source code is as good as documentation. I feel like lots of devs convince themselves of this because they like writing code and don't like writing docs, but I never found it to be true in practice. Any time I have to use an API or tool that has tons of missing or obsolete docs my heart sinks, partly because rummaging through source code is slow and tiring compared to reading a well written document, and partly because it tends to be a proxy for low effort work in other ways. The claim that docs are always obsolete is self-fulfilling. If you don't put in place procedures to ensure they're kept fresh and good then they'll rot, and then that's used as a justification for not putting in the effort, so it's a vicious circle. Again by analogy to testing, competent teams don't just let devs comment out tests instead of updating them, it's just not culturally accepted in high-functioning environments. Same can be true of docs.
This has the added benefit that you can have some back and forth to get to the root of the question before documenting it.
The key thing is that you have to make sure to actually do the step after (transfer to docs), even if it's a quick copy-paste with minimal editing.
Maybe it's because I've done so much reverse-engineering, but I reject that claim. It's quite possible to start with a binary ROM dump and build up the theory of the code. If you have source code that's even better. And the whole point of design documents is to explain the theory.
Curiously, the book quoted at the top of the article gets this correct: "Revival of a program is the rebuilding of its theory by a new programmer team." But the article says that the only way to rebuild the theory is by writing new code.
Samba is a great example of how this is necessarily an imperfect model, and definitely not an optimal process:
- https://news.ycombinator.com/item?id=488089
Development as theory-building is intended as a metaphor to understand the impact of the collaborative mental model that is created as part of any complex system, and the losses incurred when this mechanism is not appreciated.
We were often able to make better models than the assumed original authors (at least the software that used said protocols was much more performant due to better packing algorithms), so I’m unconvinced that reverse engineering puts you in a worse state.
Fully agree that the cost of reverse engineering is large, requires exceptionally skilled people, and no company should ever willingly put itself in the position where it becomes necessary for their own software.
Anecdote: Worked for a company that had a large software component (graphical design tool) that had been written in C with a very complicated object model/macro layer, file system format etc. Zero documentation, hadn’t been updated in a decade and not a single person remained who understood it or had done more than simple bug fixes. Couldn’t rewrite as customers had huge investments in assets built from the tool.
One principal dev and maybe 1-2 months? And we had the theory of mind back, explained to everyone else and a new team of 5-6 devs working productively. Component was brought into the 21st century, and this time some documentation was written.
Naur's argument is first that there is a theory at all, and that the question of whether it can be transplanted to another person is really unanswerable. We can attempt to answer it, or build confidence towards an answer, by doing things like writing/reading design docs, having experience in the industry that shows us how other eng tend to think and design, talking to the person who wrote the code, testing our theory against the code, etc. But we can never be certain of what is in the mind of another person.
Anyone whose ever taught anything to a student, I think, has come to see this. It's hard enough to teach people how to do a thing in response to a known circumstance. The real trick is to teach them well enough to have them extend their knowledge on known circumstances to new and unforeseen ones.
And Naur, IIRC, also argues that churn is inherently bad, and that programmers are not replaceable cogs, because one of the valuable contributions they are making is holding, in their minds, the theory of how the code solves problems:
> More generally, much current discussion of programming seems to assume that programming is similar to industrial production, the programmer being regarded as a component of that production, a component that has to be controlled by rules of procedure and which can be replaced easily
That if a new team takes on a codebase, they are rebuilding the theory and reviving the program.
The new theory may not be exactly the same as the old but it is probably still adequately compatible.
In a sense, the new team is reverse engineering the theory, from the source code. Even though this is a much easier task than doing so from a binary.
> A very important consequence of the Theory Building View is that program revival, that is reestablishing the theory of a program merely from the documentation, is strictly impossible
> […]
> Similar problems are likely to arise even when a program is kept continuously alive by an evolving team of programmers, as a result of the differences of competence and background experience of the individual programmers, particularly as the team is being kept operational by inevitable replacements of the individual members.
Reading documentation & talking to the original dev are inherently the same thing: we're using some medium (written docs, spoken word) to attempt to convey information.
I think the biggest benefit has been that the tests are isolated from the implementation details so we can always be sure everything is still working without having to refactor the tests themselves.
There is still a learning curve for new people because they don't know which of the multiple conventions in the code are the presently-agreed-upon ones, and that is where we could probably use some better documentation, but I think the end-to-end tests really saved us from the idea that some knowledge is lost and we might need to start over.
- Get the “change engine” running smoothly (productive and reliable local dev environment, reasonably fast/reliable build/deploy jobs)
- Get people understanding, documenting, and teaching others the high level functionality and architecture
If you skip those steps, and hop straight to new features, you’re just asking for regressions, outages, poor architecture/design decisions, and just a very slow pace of work. But invest in those 3 up front and you can get to a point where you’re evolving the product well, with few regressions, at a good pace, even without vets to teach the newbies.
Quips aside, software life expectancy matters, so total documentation approach is not something that works for everything. But if your software has generations of developers, maybe it's worth thinking about it.
That or a total rewrite every skip generation. I can see both working, and maybe total rewrites are a better approach in a world of ever changing requirents.
Thing is, documentation is great to capture static knowledge, and a evolving software is not static by any means
If I had to pick and choose, I'd say written requirements with change management for every domain object APIs within the software object model, and anarchy underneath.
The code conforms to the requirements, and not the documentation conforms to the code, so to say. Contract driven, in a way, but only where it's relevant.
There are many open source projects with great documentation and they manage to keep abreast of changes.
This attitude among programmers that documentation is hard, so don’t bother is just that - attitude.
With varying degrees of success.
Shameless self plug:
We just did an episode on Naur's Programming as Theory Building in the most recent episode of the Future of Coding Podcast.
I think Naur was just a bit to vague and assumed background knowledge of Gilbert Ryle where most people don't have any. I think a lot of people finish reading the paper and don't realize what Naur's notion of theory is or how his radical conclusions follow.
What do they realize then instead?
I spent several weeks going through about three books on Ruby on Rails and working endlessly on tutorials, etc. But I got nowhere and I couldn't stand it.
Then one day I read the Django documentation and it took a completely different approach. The approach that the Django documentation took just made sense. It began by explaining what the problem was from a high level and then explaining different solutions that had been tried over the years and what their drawbacks were and how this led to further approaches, and so on. By walking me through the system that way it made Django clear and sensible to me.
All the Rails stuff I had been reading seemed to assume that I already knew everything but just needed to understand some magic incantation. Django treated me like a child, which meant that it actually treated me like an intelligent adult.
I'll never forget the experience of reading that Django documentation. It was crystal clear and it changed my life.
This all happened around 2012.
* If churn is lethal, shouldn't we expect the center of the software industry to be in a city where people take a more conservative approach to their careers, sticking with a single employer for 10+ years?
* If churn is lethal, wouldn't we expect a stronger focus on retention of software engineers? Instead, it's common to offer new hires generous packages, and common wisdom is that constant job hopping is the path to high compensation.
The frequency of job hopping is inversely proportional to the time spent working on a career.
Gaming the system for a large salary early on without much experience only hurts the employee with burnout and getting replaced with better developed talent more deserving of the role.
Applied to this, that theory is very simple. It's enormously difficult and expensive to keep old code running, especially when the engineers who originally wrote it have left. Not only is it difficult, but the engineers who have to do maintain it will hate it, churn themselves and you'll be back to square one. So, solve the easy problem. Get your new guys to write something new, and give up on the old software once all the people who knew how it worked quit. And it's easy! No one left at the company is going to argue to keep the code. The new people will enjoy writing something new. Oh and people will be amazed at how happy and productive your team is - because you literally just choose not to do things that are difficult and no one appreciates.
But you may ask, what if the product is essential, you can't just abandon it. Well, call me flippant but if it were essential you shouldn't have let the engineers quit. And if you're really going to saddle me with it, I'll quit too. Then you still don't have your software, and it's not my problem - it's yours.
I think this needs to be said more carefully. For instance, problems could become manageable when a well-thought-out mental model is built. On the other hand, they could become easy when the problem itself is simplified. For me, the e-voting problem fits quite well in the picture where thousands of systems are being built with cute mobile UI but, in the end, are really holding on to the assumption: trust us, we will be honest.
The last program I built was coded around characters from Sherlock Holmes, by making the code like characters that had to interact in a certain way, it helped me maintain a mental model over a longer time. It genuinely made the code more enjoyable to work on, as it is a story now
I worked for a period in a consumer product OT shop. I was a consultant; not a 2nd gen. It's not often you discover you have access to a quarter $mil of OT equipment just lyin' around as hot spares sitting in a lab down the hall; and it all talked to the hot spare L2 which we used for development.
Technically, I was working on the L2 system. But I needed to write test plans. When I showed up, they showed me "the wall" which was three ring binders (1/2 to 1 inch thick?) of "test plans". Each binder was a test plan. Yes.
Over the better part of a month I assembled enough stuff in the lab to run product "end to end" in the best environment we could simulate.
So, after meetings, I was able to write a test plan which referred to an SOP for some portion of its setup and then deviated (a "variance") for the actual test. Tests took longer to execute, but a hell of a lot less time to write.
To set this up, I interviewed the actual line personnel. By the time my tour was done I had to schedule time in the lab because they were using it for training and general "what if" screwing around.
I locked myself in a really hot closet behind the cafeteria with several pieces of our soon to be purchased hardware and started coding. The whole thing ended up amazing and I am still proud of it to this day. =)
Some of my primary goals where simple: No manually edited configs, ever. If you swap a mother board there should be no mac address tracking, no serial number editing, etc. If we can detect it automatically we did and updated the machine database automatically. Hardware techs could simply walk out, swap a drive, and walk away and we would track that.
So we wrote a tool called vex that would export all the machines various bits and bobs. Anything we could access, serial numbers, mac addresses, etc. So once the machine was running we knew where it was, what it was doing, etc. The biggest challenge was getting it running in the first place. The plan was to use DHCP option 82 which includes port information when forwarding DHCP requests to an upstream server. Thus our dhcp server (written by an awesome coworker in like two days) was 100% stateless. It knew the IP range for the datacenter, the ip range for the rack, and the port that the request came from and could respond with a static IP every time. Swap the mother board and it wouldn't matter because the machine would still be plugged into the same port on the switch. Each machine would then request a tftp boot image from the server listed in the dhcp reply. This server was smart, it would look in the machine database to see what state the machine was in and where it needed to go "next" so to speak.
Step 1) Machine has never booted before. It would go into an identify utility that would allow the installer to verify that each machine was plugged into the right ports. The tech could watch the ipmi identify lights on the machine as it activated each in the rack in order and if any where out of order network cables needed fixed. From this point forward we knew that port 3 was the 3rd machine in the rack on the prod and ipmi networks. Once identified the machine would automatically reboot.
Step 2) The machine would run memtest86 to ensure that memory was valid. Sadly I didn't have time to make it automatically update with results and reboot so this step required manual verification when I left. A engineer would check IPMI and mark the test successful.
Step 3) The machine would run stressapptest to push the CPU and memory to the extreme. This allowed us to ensure that they would not overheat and that the rack could pull the load needed to get all the machines to 100%. Once done it would mark itself successful and reboot.
Step 4) The machine would run fio to ensure that every attached disk was in good state and fully functional. Again, it would then reboot automatically.
Step 5) The machine would install our default operating system assigning hostnames and such via parameters from the database.
Lastly once the machine booted into its "real os" for the first time it would setup puppet, install all the back packages, look into the machine database for its assignment and then install all the various bits ad bobs required for it to do its selected function (hadoop, web server, dhcp server, puppet server, etc)
Everything was driven by the machine database, and you could mark a machine as needing burn in and it would repeat the testing steps. Super useful if you suspect a machine is bad in some way but don't have time to debug. Once running vex would export all the details of the machine as well so we could track failure rates, hardware events, etc.
Best part is that the lab ran the same software as the datacenter so we could always test the various bits of this process to see what worked and what didn't. In fact I added several optional steps to do things like get performance profiles and build all kinds of cool graphs from tests on demo hardware.
I ended up bootstrapping three datacenters and all it required was an "Ark" which was just a single server with puppet, dhcp, tftp and audubon on it ready to setup all the other servers in the facility. During our initial power up we had a DHCP server doing something like 100-500 requests per second sustained over the course of like a half an hour (until the memory tests started)
Sorry, not sure why that memory jumped out at me but it seems you did something alike and it reminded me of how much fun it was to be involved in mine. =)
I think this is about myth and ritual. Resist the impulse to build it to something other than the Building. There wasn't any "cloud" in Colorado in the 1990s.
"People lose focus" is an anodyne when they never risked their lives to begin with.
Once the line was built, the meat moved in to operate it. In fact, the meat had standups: it was the first time I ever experienced a standup (and I wasn't meat, so I wasn't invited to speak unless I submitted an agenda item ahead of time).
The meat knew how to operate the line (and operate it safely). Although the culture of safety and quality was everywhere, the operational practicalities had been lost at the control system level: the meat were the experts, and we asked them questions a lot. But nobody asked questions about how to run the line, because nobody was going to change that ever!
So those were the kinds of questions I asked the meat and the level 1 engineers in order to construct an end-to-end simulation in the lab. It created a relatively safe shared environment where people could perform experiments and test their understanding of how things worked and appeared to work at the different levels.
The level 1 engineers were a pretty easy sell and helped a lot. The meat were generally willing to answer questions, and moved in once they could see it working. I actually authored the SOP by typing up HTML docs in Notepad on a file share. HTML was a new thing for them.
PLCs, custom electronics, servos, consoles with switches and knobs, very expensive printers (probably running Windows unfortunately).
Like some especially stupid examples:
- firing the main tech lead which had worked month of unpaid overtime (not for month, but the accumulated equivalent of month of work) because he supposedly didn't work enough during the insolvency proceeding/first week after it.
- cutting the cost of renting the storage system with essential data without making backups
- firing most people which where important for the main product
- firing people in such a bad way that most of the rest quit, too
- deciding it's more important to keep marketing people then people from the development team in a situation where the insolvent company had multiple bigish contracts they just where missing a few month of work to be able to deliver to and interests from other customers and the buyer being the main potential customer buying the company to get the product
https://press.uchicago.edu/ucp/books/book/chicago/T/bo603536...
Programming and Tacit Knowledge
https://mbuffett.com/posts/all-tacit-knowledge/
Tacit Knowledge blogpost series, Commoncog
https://commoncog.com/the-tacit-knowledge-series/
Acquiring and Sharing Tacit Knowledge in Software Development, 2013
https://www.researchgate.net/publication/264125152_Acquiring...
wiki says:
“ Tacit knowledge can be defined as skills, ideas and experiences that are possessed by people but are not codified and may not necessarily be easily expressed.[5] With tacit knowledge, people are not often aware of the knowledge they possess or how it can be valuable to others. Effective transfer of tacit knowledge generally requires extensive personal contact, regular interaction,[6] and trust. This kind of knowledge can only be revealed through practice in a particular context and transmitted through social networks.[7] To some extent it is "captured" when the knowledge holder joins a network or a community of practice.[6]”
The distinction appears to be the implication of OP that the ‘theory’ is not communicable, thus “first generation” workers need to be around. But you can clarify (am interested in your views) (a) what’s your crit of TD and TK, and (b) the matter that I missed, the “something very important” (which is not TK transfer).
p.s. “we know more than we can tell” is pretty much the tldr of T.D.
1. "We know [the manner in which we carry out a task] more than we can tell [...but are unable to articulate the procedure by which we do it]." 2. "We [can do] more than we [can explain in writing or verbally]."
The second statement is uncontroversial. No one would say that the ability to ride a bicycle logically entails the ability to explain how we do those things in writing such that, by reading alone, someone could learn to ride a bicycle.
But this is only mysterious if you consider the second statement to be a paraphrase of the first. I don't think that it is. The first statement suggests a mystery; it's as if we are always following rules when we act skillfully, but are sometimes unable to articulate those rules.
But this is a mistaken view. Not all skillful performance is rule-governed. In fact, most isn't (statement two). And that is easy to forget, but does not require a theory of "tacit knowledge".
The straightforward parsing of that sentence for me is: We are not capable of completely expressing our (personal) knowledge with language.
The consequences of the logic contained within Naur's essay are far stronger than I think the article understands them to be.
But the content more than makes up for the style!
People leaving means that there's nobody there who can fix the decay when it happens, though.
1. It takes more time for the rot to show.
2. Twitter devs were amazing at building really solid software
3. The culture of twitter was really that 1 person did the work and 9 others watched, enough of the productive devs stayed.
4. The effects of rot are overestimated.
I have no insight into how twitter worked as a company before Musk's takeover though, and I still think it is quite likely major problems will occur in the next couple of months.
Minimizing churn is just one aspect of that commitment. If the project was always a mess it probably wasn't as important to the business as the developers believed.
It is trivially easy to think of a structural counterexample to their primary point, which is how experienced senior engineers can inherit production codebases without documentation or the original team that wrote it and are able to successfully operate, understand and improve the entire codebase. Is there a gradient of pain? Yes. Is it possible to inherit a codebase that is falling apart, without the resources to get the ship back on track due to organizational issues? Absolutely.
However, it doesn't change the fact that every year, significant quantities of senior engineers do something that the author does not believe to be possible. This isn't to say employee churn is particularly health for software companies; it's often not. But that really varies on a case by case basis.
Documentation is helpful but the intangibles that come and accrue to just someone or someones who have been in the trenches for years and years can't be documented and when they leave it can be fatal at least for a team/unit.
Add to that the onboarding cost and the cost of hiring: take in-house or (worse) external recruiters and their salaries/commissions, add to that senior engineers taking time to interview candidates and both prep for the interview (hopefully) and then write up detailed notes afterwards -- count not only their time doing this but their time NOT mentoring a junior engineer or working on something else during that day (yes this might sound like double counting but senior engineers really do make 1+1=3 so pulling them off something is extra costly imo), and then add the expense of the employee being hired, all their gear that they'll need, and then the 3-6 months until their productive and all the help/training they'll need to get to productivity AND THEN imagine all that and then person leaves after 6 months to a year: how much does that cost? now multiply that by your attrition rate... it gets hella costly.
He also wrote that over time a software system, through dynamics of the programming process, the rate of change tends towards being statistically invariant. This is because, he said, the team involved in the system, including not just the developers, but also the users and others, have a finite ability to maintain understanding of the system, and excessive and too-rapid change outpace the ability of the people to maintain understanding.
A Sixty North blog post[2] provides some interesting graphs showing the measured effects of Lehman's laws, including one showing that after 5 years, almost 2/3rds of the code is written by people who joined the team after the original team members left.
1 Metrics and Laws of Software Evolution - The Nineties View https://ieeexplore.ieee.org/document/637156
2 https://sixty-north.com/blog/predictive-models-of-developmen...
While it's possible that there are systems out there that can't be understood, it's plainly false that - to paraphrase - code dies and must be replaced when the original developers are gone.
A couple examples from my direct personal experience:
1. I work every day on code that's 20-30 years old, in use in production by hundreds of millions of people every day. There's no one around who worked on it originally. And yet it works, 100s of pull requests a day, and customers continue to pay us to use it.
2. I took on a volunteer project this fall, working on a ~million line distributed system that runs on clients at about 30 sites and integrates with 3 cloud services. No original developer is working on it. For the client code, it took me about a day to understand how it works and make meaningful changes.
The solution to the problem, at least according to the article, isn't "no churn ever", but "low enough churn that the newer developers acquire an accurate mental model of the system."
I was just wondering if it would be good general practice to refractor piecemeal every couple of years. This would (in theory) reduce tech debt, and if the refactoring is done by relative newbies, you ensure the base is understood and stays fresh.
I haven't hashed out or thought this through yet, so happy to have holes poked in this idea
"Team stability is vital for software development. Each team needs to be composed of a majority of first-generation developers. Too many second-generation developers and the first generation gets overwhelmed, and work stalls. The work slows down either because too many core tasks are waiting on the first generation or because the second generation keeps having to rewrite components from scratch because they didn’t understand the theory behind the original component. Too few second-generation developers and there is no renewal—each developer that leaves the team is a potential catastrophe.
Many teams in the industry constantly rewrite parts of their code. Not because they keep figuring out better ways of approaching the problem but because nobody on the team has an accurate mental model for how it works and how the code fits in with the rest. If you can’t hold onto the original team, if you grow the team too quickly, you end up running to stay in place. Code keeps getting written without any improvements of substance to the software itself."
I'm skeptical that these companies were so bloated that they needed to lose large percentages of their headcount.
The pressure you face in communicating this theory is time to market. I am obsessed with cleanroom, zero defect software, and literate programming. However, I have only worked on one project that these tools would have been relevant for. All others, the code/system moved on faster than the time it would take to get it communicated.
The real measure of programming languages to me then becomes raw productivity. If we could make programming literally a 10% or less task, and documenting and communicating the 90%+ task, I think software companies would do very well. This bodes well for things like ChatGPT/copilot.
That's the easiest way to learn it, but it's not the only way. After a while, it even becomes fun to read through other projects (some of your dependencies, e.g.) to see how easy it is to develop this 'theory' (even if it is fleeting; in the case of dependencies, you'll at least keep a rough idea of the cost of that dependency, relative to it's value)
Most of them are operated with a startup mentality, aka we will be around for 18 more months so let’s optimize for that.
All of the employee benefits are incentivizing them to stick for the short term and then move to other pastures.
Or you know they could instead offer high 401k, pensions, career options for people over 55y.o. etc.
But it is always easier to burn though fresh grad meat, and let the dumpster fire for someone else to deal with.
Hell, I would recommend Java over nodejs for this reason.
All jokes aside, time and time again have I seen web projects erode away with 1000s of employees taking a quick hack or asking for large refactors and then leaving soon after.
I have seen teams and divisions that operated like clock work suddenly descend into chaos after 25 percent of the team left for higher pay. Once that happens it is over. Everyone leaves because work becomes terrible for everyone left.
You don't need to know how it works to use it. And if it really needs changing you can make a new executable with a new version number in its name. That way legacy code can continue to call on the old still functioning version while other peaces of code call on the new version.
This in and of itself pretty dreadful but I have been wondering recentl whether or not abstract thought is quite so. I'm not quite sure.
High level capitalists (such as VCs) treat rare people unfungibly e.g. Paul Graham “the success of a startup is almost always a function of its founders” or http://paulgraham.com/founders.html
Edit: also fungibility is by no means a capitalist idea, and it is a central concept to some non-capitalist doctrines. What is equality?
But also reducing to single entities misses the forest for the trees. Commodities and there exchange value is a social construct that naturally appears as we exchange goods and ultimately human labour time.
The high skilled worker's cost is not just the time now but all the time spent to get to that level of expertise.