My guiding principles after 20 years of programming (2020)
alexewerlof.medium.com
alexewerlof.medium.com
While I agree with this in general, I find that to reall fully understand a problem, I need to attempt to code, or at least formulate, a solution to it.
a) because when I break down a problem into its code-able component parts, I learn a lot about it
b) because in the process of then actually implementing these parts I often discover edge cases or undefined cases (especially in naturally grown business-logic)
c) because what the problem actually IS, is often not that clear at the start of the problem. Yes, in an ideal world, changing requirements would wait until the next version, however, sadly that's not what happens in the wild.
Personally I have never 100% solved a problem before writing any code. For me it really the other way around. By writing the code I better understand the problem.
I guess what we're dealing with here is that different brains solve similar/same problems in different ways. I guess its also the reason that advice such as "Never start coding ... unless ..." doesn't work for everyone.
I think the main difference is between human brains and machines. Biological systems operate with a kind of "fuzzy logic" by default...glancing over edge cases, smoothen out discrepancies on the fly, filling in missing information with context knowledge or assumptions, etc. Being imprecise is not a problem, in fact being able to handle the imprecise, is what keeps living things going.
When we describe a problem to a human like "take that crate and put it in the next room", we know that he will fill in the gaps (hopefully) like not assuming that the cleaning closet is the "room" we meant, even tho its right next to the starting room, and its technically a room.
Describing this to a computer is different. Edge cases need to be considered. What if there is no crate? What if there are many, which one does he need to get? What does "in the room" mean? Does the orientation of the crate matter? What do I do when the room is full? "Next" on which side of the corridor? And so on, and so on.
Only in having to describe a problem in an algorithmic way, can we fully appreciate all of its parts, and the information flowing through the process.
While it is possible to completely understand the problem before writing a solution, it takes much less time to just build a prototype, analyze it to see what mistakes you made, rinse and repeat.
Note that the third principle of the "unix philosophy" is[0]:
Design and build software, even operating systems, to be tried early, ideally within weeks. Don't hesitate to throw away the clumsy parts and rebuild them.
[0] https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch01s...So in that sense we could rephrase the idea as do not commit to the code you write until you have a good understanding of the problem. Use it as a learning tool.
> You need to progressively go through the code-test-improve cycle and explore the problem space till you reach the end.
So the author seems to agree, somewhat contradicting his own point.
This is especially bad when the prototype works, but doesn't work well. They think you just need to polish things up a bit when really, you need to take what you've learned and build the real deliverable.
Another example perhaps as to how the software engineering field still has significant maturing to do vs. e.g. mechanical engineering.
I believe people underestimate the power of a good IDE.
Definitely. And in some parts of the tech world, people actually deride the usage of an IDE! "If you're not using vim you're a newb," kinda deal.
Which, sure, vim is great! And you can get a lot of decent plugins that can make that workable. But gosh IDEs provide so much powerful functionality, why would you make your life harder on purpose by not using them.
That attitude does seem to be fading though, in my more recent experience.
I share the sentiment, but for completely other reasons.
Human mind is very limited - it can only hold so much complexity at once before it starts making mistakes and oversights. IDE's raise that bar of tolerable complexity, making it easier for people to build enterprise-level Rube Goldberg's machines just to keep the software working.
Using Vim (or any other non-IDE editor) forces me to keep the software simple, because otherwise I can't understand it. And in my experience, keeping software simple (long term) is much more important than keeping software working (short term).
Of course, sometimes we have no choice but to meet the deadline. That's where all those bells and whistles really come in handy.
What do you mean, "sometimes"?
“I love deadlines. I love the whooshing noise they make as they go by.” - Douglas Adams, a man who was very familiar with the many varieties of deadlines.
> For macOS, switch on the VoiceOver and install and set up IntelliJ IDEA. However, for a full screen readers' support, we recommend Windows.
I've never tried the Windows version, but the VoiceOver version is unusable.
[0]: https://www.jetbrains.com/help/idea/accessibility.html#scree...
So with that being said, why do I use vim in a terminal emulator, instead of a modern IDE? Here are my most important reasons. Bear in mind, these are extremely dependent on my personal taste, modus operandi and thinking.
1. Simplicity helps me focus. vim hits a perfect mark between simplicity and feature-richness. Interfacing it with more complex systems such as LSPs, Linters, etc. is almost trivially easy.
2. I like building own tools and adapting existing tools them as I see fit. vim is pretty much perfect in that regard, not just because of the power of vimscript, but because how easily I can integrate tools I wrote myself into it.
3. It's absolutely trivial to set up: I copy my ~/.vim and that's it.
4. Once I figured out jumps, markpoints and linescripts, it allows me to do absolutely crazy things in codebase navigation
5. It works over ssh
6. It runs instantly and has a negligible performance impact
7. I can seamlessly integrate it into other CLI tools that require an editor, providing my default work environment in every situation.
8. Since version 8, I can actually use it as a terminal multiplexer, which is just crazy good for my workflow...I often have several split tabs to edit/navigate the code and a terminal tab controlling deployment and testing on the remote machines...all in a single terminal emulator window.
So yeah, why do I like vim? Because its flexible, fast and extendable, works everywhere and does exactly what I want.
Sure, as long as it's not on my dime! :-)
vim et al are great and I know skilled users can get very productive in them, but I would not push it as a default environment for incoming developers. The amount of force-multiplying functionality in the stock install of IntelliJ or VSCode is very good.
I would not push any default environment on incoming developers unless there is a very good reason to do so (eg. custom graphical lowcode tool that only works with a specialized editor). As I said, one of the beautiful things about programming is that the craftman gets to chose the tools.
Well not so fast, first you'd have to answer 100 questions to configure the IDE for a 'project'. Only then could you edit your file. Slowly. One. Single. Character. At. A. Time. Waiting for the IDE to become responsive again after each character.
Nowadays, however, the hardware has caught up and some IDEs are actually usable most of the time. And they're indispensable on the big convoluted projects we now deal with, that have tons of dependencies and convoluted ravioli code from OOP hierarchies.
I do kinda miss working on projects that were simple and clean enough to not need an IDE. But it would be terrible to go back to not using one with the code we wrangle these days.
1. Start small, then extend. Whether creating a new system, or adding a feature to an existing system, I always start by making a very simple version with almost none of the required functionality. Then I extend the solution step by step, until it does what it is supposed to. I have never been able to plan everything out in detail from the beginning. Instead, I learn as I go along, and this newly discovered information gets used in the solution.
I like this quote from John Gall: “A complex system that works is invariably found to have evolved from a simple system that worked.”
From: https://henrikwarne.com/2015/04/16/lessons-learned-in-softwa...
I see this as a basic skill, that we should aim to master at some point, even if we may never truly master this art.
If we do not move carefully around assumptions, we will have to refactor things later, which means more work. Of course moving carefully can also consume time. At some point it may become a tradeoff.
Me too. Sometimes when I don't feel like I really know how to solve a problem, I'll just write some really hacky code to try to get an answer. Just anything that moves the problem forward.
Then once I understand what I want to do better, I either throw out that code and start clean. Or I just refactor the heck out of it until it is in a good state.
Not only is it more interesting to me to try three different ways to tackle a problem, but I have been burned when the two weeks of whiteboarding missed something and I'm back to having to iterate anyway.
To be sure, I do a little whiteboarding, but generally it might be about 2 hours or so of sketching out ideas, major structures, code flow ideas.
I generally was nodding along to most of the author's points though.
I definitely have grown to divorce myself from my ego and always try to not only try to shine the spotlight on my younger coworkers (new engineers) but try to give them "ownership" of key pieces to allow them not only some sense of autonomy/ownership but a sense of pride as well.
That does go slightly against some of the author's points about collaboratively working on a project. Engineers need a part of the code (let's say an image cache manager, as an example) that they can "own" though in order to grow. You don't want an engineer to always have "training wheels" on. (And frankly, I think this is one of the things I dislike about code reviews, I think it dis-incentivises autonomy.)
The team though, let me be clear, is the most important part of any project, not the individual luminaries. The "team" though, to a degree, needs to have engineers who feel an ownership stake in pieces of the product.
(FWIW, I have probably 35 years of programming experience, ha ha.)
first time to understand the problem
second time to understand the solution
third time to do it right
If I did all the beginnings first, I'd then go back to doing the middle of A and realize I did the beginning wrong and have to go back to fix the beginnings of B and C.
I usually so a vertical proof of concept where I tackle each step I understand in a unit-test. ofc it's no unit test- the test framework just serves as an quick entry point where I can test different approaches next to each other or different versions/variants of each approach.
like a repl with the benefit of an easy view of my "history" with the ability to step back in time.
Obviously it's helpful to formalize the problem! This is no different from manipulating mathematical equations on a piece of paper. =)
you also need a well-thought out outline for any larger work like a novel, or, it will be a disaster, or at least not be anywhere as good as it could have been
credentials: creative writing for decades and experienced 1sthand tradeoffs of each. my verdict? seek the Hegelian dialectic. ;-)
related semi-tangent: with fiction I also adopted the rule to write the Ending first. because, for me at least, it then crystallizes what sorts of Middles and Beginnings might lead to it, and make the most sense or have the biggest emotional impact on the reader. which then practically paints out the character journeys for you, as the author. very helpful tactic
It's more common than you think.
Hasn't been the case for me so far. Every story and essay I've written started with an idea. If there is an outline, it gets thrown away page 2 and never returns. On my second novel and this still hasn't changed.
My writing is actually better this way because I start with characters and plot emerges as they deal with the forces around them. I never know how they will react until they do.
I program the same way. Write a bit, revise, write a bit, revise. Drives some people nuts if they see the process, but the end result has been just as good or better than my coworkers throughout my career. (I'm a huge fan of tests. My revise step includes adding tests.)
In my experience they'd only relucantly agree if and only if it's "do this or die". Which then happens under time-pressure or under stress factors of other kinds.
"if it already works, why fix it?" (because it's making developer's lives much more difficult than they could be? --"but how does this increase revenue?" --errhmm... because incresaingly difficult to maintain? but they don't care about this "that's your job")
> "if it already works, why fix it?"
I suppose one attempt at a fix for this would be to make the user interface of your prototype the least prioritised part; maybe even go out of your way to make it ugly and clumsy, so the thing as a whole looks more like a prototype and less like a "solution".
In order to implement a good solution you need to understand the problem well. Often, the only way to reach that level of understanding is by trying and failing to implement it.
These days, with modern refactoring and incremental development, it's less throwing away a whole program and more gradually refactoring it until little of the first code remains but I think the observation still holds in many cases.
One: coding is a way to play around with ideas, equivalent to a back of the napkin calculation, a diagram in the sand or a pencil sketch of a part. We use code in this scenario more as a way to write down our thoughts and process. But rather than writing in just pure English and writing an essay or a set of Todos, we write more detailed "specs". "Do this three times or until this value is zero" is also written in code and perfectly understandable in code too.
Two: code is used to run by a computer to produce an outcome. In this case, the back of the napkin sketch IS the part but more thought out.
Society is so used to there being two material objects between the "plan" and the"final product" that we forget that code is different. With code, the plan and the final product are of the same form. Of the same physical space.
> While I agree with this in general, I find that to reall fully understand a problem, I need to attempt to code, or at least formulate, a solution to it.
I'd go as far as to say that programming is a good medium for expressing poorly understood and sloppily formulated ideas. Oh, wait, someone else already said that...
1. Code is a liability. No code? No bugs. The best commit is one that removes unnecessary code. This includes dependencies.
2. State is a liability. Multipliers for: hidden or non-obvious state, shared state, externally (by actors you don't control) accessible state, concurrently accessed/mutated state. Often the worst offenders are environment settings/configuration, such as Windows Registry, environment variables, installed dependencies, daemon services, etc, scoring full points.
3. All publicly observable behavior is a liability. Also known as Hyrum's Law: "all observable behaviors of your system will be depended on by somebody", or https://xkcd.com/1172/. Huge multiplier for systems with a long expected lifespan, and another huge one if you guarantee backwards compatibility to paying customers. Program under this assumption, and if you can hide or control your internal behavior, do it. E.g.: maybe don't output results reliant on a HashSet order - sort them.
None of these are hard 'rules'. Often the liability is necessary, or worth the saved effort or gained feature. But be aware of them, and minimize them where possible.
Don't keep adding new technologies to your tech stack, it increases complexity and bus factor. I'm saying this as a warning as well because I've witnessed numerous projects where they switched to a new technology or tried to rebuild their tech stack, only to go back once the consultants and self-employed folk got bored. They hired external people to accelerate without having them focus on handover and building up their own IT department.
I would much prefer shuffling them in that case. Less expensive and reduces the size of the public API. (Returning items in a specific order, whichever it is, still is a sort of promise.)
Then a few releases later the database switches from a B-tree to a hashmap for the GROUP BY internals and now your output is no longer sorted.
Ugh, a former team I was on had that to the extreme. We were customizing open source software for internal use. A lot of operators had (over years) developed scripts that would just SSH in and use a CLI to interact with the software, using regex to parse output. We once thought we were safe adding a new line to the output rather than modifying an existing output line. Nope, someone somewhere used a multi-line regex.
Kicking everyone's automation out of the shells of those boxes was a multi-year project that is, as far as I know, still ongoing.
Your product might still work if it has a second of lag after every operation, but I won't want to use it. And if there aren't any decent alternatives, I am going to experience irritation and sadness on a continuous basis.
Jonathan Blow said[0] (5 minute interview) that software authors have an ethical responsibility towards their users. How many people use YouTube, or Windows? The time wasted might be small on an individual scale, say a minute a day, but multiply it by a billion people (eg. YouTube or Windows) and that's 16 million man-hours per year.
I'd argue the same responsibility exists just as strongly even if you have one user! Do you want to frustrate her, constantly waste her time? Of course not! She is not going to be very happy with you if you do that :)
[0] Jonathan Blow on Success and Ethics in Software Development https://youtube.com/watch?v=k8gIJOy0c2g
The more you have of it, the more you can afford to do. This is interesting to think about. There might be stuff we don't do, that we rule out because we don't even think we can afford doing them. A slightly milder effect here is that we tend to make things more structurally (architecturally) complex and expensive, just because we don't even think of making them fast in the first place. Both of these effects impose real-world limitations and costs.
Secondly, if you don't have enough of it, then you tend to be constantly distracted and limited. Anyone who went through financial hardship for a time knows about the mental, physical and social tax this imposes. I think it is useful to think of performance that way. Or to turn it around: if everything we do and compute was incredibly fast and reliable (also an aspect of performance) then how would that change our behavior, well-being, productivity?
Side note:
Garbage collection is often used as an example in the way I describe above. One can "afford" to use it, or not. But I think this touches on a rather special aspect of performance, which is a broad term to begin with. It affects the overall memory footprint and variance (GC pauses) but doesn't necessarily affect overall latency and throughput, which are more generally applicably aspects.
At one time, people would plan "interactive" applications where the response time turned out to be >10X or more than what they had anticipated. As an example, imagine clicking on a dropbox and having to wait 20sec for choices to appear -- and then also finding scrolling down a list impossibly slow.
That would call for reconsidering the approach to presenting choices, in this case, and in general it could call for a significant redesign in advance.
EDIT: I suppose this could still be most obvious in games. Could you successfully plan a visual game design while being 10X miscalibrated in how fast an engine will support updates?
Well that's why usability is its own item. Whatever is needed for usability is ranked as important by the list. If indeed your users find that their button clicks are just so very fast you need handrolled assembly to keep up, then writing that assembly is vitally important. Otherwise? Probably not so much. Performance for its own sake is last priority.
Now obviously there's a coherent argument that in general performance isn't considered enough when evaluating usability. That's totally fair, and probably true.
Unless of course the pet projects happen at work during working hours, but finding a place that allows that is probably a whole different kind of beast.
I've seen to many "interesting" technlogies being crammed into projects just because devs needed them on the CV.
If people can test out everything they want without having to claim that they need it on a project it can save ourselves a lot more than 15% on any project or product that lives for more than a year or two.
At work we are still sometimes suffering because of how cool Redux once was.
I would like a competent plumber and if he does some fancy work in his spare time and gets even better than great.
But I would raise an eyebrow if beside my work he will also want to be paid for 6 hours he spent in the weekend, experimenting on his bathroom...
I'd expect if the plumber worked for a plumbing company they'd probably invest some time and money into skills growth, and pay for it out of the money they charge you.
And I truly think it’s the same for your codebase.
If you do not offer the possibility to learn to your employees they will try to learn while working on a project which is where problems come from : speculative programming or hype-driven-development.
How many side projects, at Google, and then at other places, didn't turn into runaway successes?
More importantly, if you tell management "you should let us work on side projects because it could make the company millions of dollars", watch how fast "did you use your 15% time to invent anything that is marketable this week" becomes a part of your performance review, and your "side project for personal development" becomes "PMs breathing down your neck asking for progress and status".
But that's the point. Your run-of-the-mill incremental improvements with a fairly certain ROI of 0.5 % or 2 % is what you spend almost all week doing. Then you have a few hours of doing whatever you want. And a very small fraction of those things will have a ROI of thousands of percent. And you won't know when it happens until it's happened.
Beyond that, I don't care if it's cancer research, self-driving bicycles, an email client someone else at the company uses, or a script to automate some commonly performed actual work task.
Work is when someone tells me what to do or where/how to do it, which is why they usually have to pay me obscene amounts to make it happen.
To have pet projects is to break out of this daily slow death and to keep learning and being intellectually challenged while keeping up with the industry.
I used to be a maths teacher and I love maths, and would do it for fun outside of my job. That in turn often helped with the job itself, and certainly helped to keep my interest in the subject alive even when 90% of the job was teaching fairly basic stuff that wasn't particularly exciting.
I'm now a software engineer - I'm much happier in my job, but I don't love writing code enough to ever want to be doing it outside the 40 hours a week I'm paid for. I'm slightly jealous of the people who do, because I know first-hand that it's a great way to do better at work and keep feeling fresh and interested in what you do, but forcing yourself to do it doesn't bring any of the same benefits.
I agree with a lot of the points made by the author (2, 5, 7, and 8 really resonate with me too) but I think this one is my favourite. One strategy I've used which is successful - but not very popular - is "readme based development". The concept is simple, if we're building something new then the first thing we should do is summarise the project in the readme. If we spend time describing the problem and scope of the project and finessing it down to a few sentences before we start coding then we should have a better chance of staying on track and have an easier time communicating with others. The bigger the project the more useful this can be but unfortunately the majority of folks I've worked with do not like writing.
Speaking of boundaries and assumptions, one of the most important realizations I have had about software development is the true meaning of modularity:
Software modules are the things whose boundaries limit the spread of assumptions.
By the way I agree with the last part. Sometimes the best way to understand the problem is writing a half-broken solution for it. You just have to be aware that what you're writing probably won't solve the problem, and make sure all the other stakeholders are aware too (that's usually the hardest part).
I've consistently outproduced peers by picking up the compiler early in the cycle and throwing away what I've done if it isn't working out. It requires a bit of self awareness, judgement and taste to know if something isn't going to work but I think the people who hate coding also can't stand throwing effort away like that.
> understand the problem
> code-test-improve cycle
I think you're first trying to understand a non-programmatic problem, a business problem, a user problem. Then you start to explore the solution to that problem. Yes, you'll learn more about the "problem" as you solve it, but the cycle explores how you're solving the problem.
That's because RBD is basically Amazon's "write the press-release first" methodology and no PHB will say "we won't use something that Amazon says makes them successful."
The pizzas come from another Amazon saying.
>Tech debt is like fast food. Occasionally it’s acceptable but if you get used to it, it’ll kill the product faster than you think (and in a painful way).
However, I would have worded it more strongly:
>Tech debt is like cocaine. It will make you unnaturally productive, until one day it doesn't.
My point here is twofold:
1. The speed with which which the worm turns is underestimated by Silicon Valley "move fast" culture.
2. The obsession with go-to-market speed in SV and SV-adjacent companies is extreme. In some sense, it's as extreme as the lets-take-blow-and-get-rich finance circles.
There is certainly something to be said for iterating quickly, and it is likewise true that engineers have perfectionist tendencies, but I think our industry has taken a good thing much too far. Going to market has more to do with being focused than with doing sloppy work. Slow is smooth, and smooth is fast.
So, if you have the time now to not borrow, don't. But if you can leverage growth to have more future resources to pay back on debt, you should probably do so.
From there, I have no good answer. Some people will take it once or twice, succeed, and be fine. Others won't.
I like the list! There are some things I would replace with other things (overall, I think I'd emphasise more general business and product development aspects more) but of course, everyone has their own list!
Just the other day I was doing a code review where the code itself didn't have a single comment in it, yet the PR had several notes for the reviewer around why something was done a certain way, the failed approaches, unexpected behavior they had to workaround, etc.
My response, "Please put all this information into comments in the code so the next person to work on it doesn't have to figure all this out again".
If code was tricky enough that it took you down several unexpected paths to get a few lines right, take the time to note that in the code for the next poor soul to work on it. (Which may well be yourself in a few months when all those lessons are forgotten).
For example: > Deprecate yourself. ... Don't own the code.
On one hand, this is true: ultimately, you want the code to be independent of you. But on the other hand, I've witnessed bad code quality proliferate in a repo many times because no one is taking ownership of the code they write: they just write enough to finish the task and move on. And it rarely gets caught in code reviews either because all the other members are operating in the same mindset too. His principle misses this side of the coin.
Over the years, I came to realize that at least for me, there is one root force behind almost everything for me, including how I code, how I interact with members, etc. It is: responsibility. I am simply trying my best to write responsible code, create a responsible product, be responsible to my team members and colleagues, etc. The rest of the stuff (like what you might call 'principles') are just specific manifestations of this feeling. e.g. code ownership: I need to own my code to the extent that I need to be responsible for my work, but on the other hand, it is also my responsibility to ensure the code does not depend on me forever. Albeit this probably may be too abstract to call a principle. In that case, I'd rather not have any.
In my experience simplicity is the cornerstone priority that nearly all other attributes stem from. Simpler solutions tend to be easier to rationalize/debug, leading to systems that are more maintainable, more reliable, and generally more secure (see OpenBSD). This is of course not an absolute rule but I've found success by always being cautious when introducing complexity and investing extra time to explore/discover simpler approaches.
The author shows they have this insight in points 2, 11, 13, 17, and especially 19, but didn't really represent this in their list of priorities. Simplicity is more than DX.
> Simpler solutions tend to be easier to rationalize/debug, leading to systems that are more maintainable, more reliable, and generally more secure (see OpenBSD).
Yeah, IF they are more secure, THEN use simpler solutions.
The other weird one is finance near the bottom. Typical engineering bubble style thinking... make sure your solution actually provides value and that people will pay for it! Otherwise you're wasting your time solving the wrong problem! The importance of that cant be understated!(unless your goal isn't to provide value of course)
Plain text passwords are a lot simpler and less secure than salted, hashed passwords. Using bcrypt to hash your passwords is a lot simpler and more secure than some home-grown, self-implemented hashing algorithm.
Also as it says he is talking about priorities, if something is simpler and less secure then choose security. If something is simpler and more secure then hey double win.
In modern OOP, with code and data all mixed up and algorithms being injected at runtime with vastly different effects, things can get unreadable very quickly.
Mixing state between a bunch of different backing stores, or not making in memory data structures explicit, makes data structures harder to discover, but what does that have to do with OOP?
If that were only possible.
For example, say you want to write a C compiler. You have a C Standard as a guide. Nobody fully understands it. And implementing the Standard is only a small part of implementing a C compiler.
Your implementation isn't going to survive first contact with a user, either.
P.S. Paul Mensonidas is the world's leading expert on the C preprocessor. That's a good indication that nobody else understands it :-)
Don't worry about it. Just start implementing the parts you do sort of understand, and keep iterating.
That means you'll always be stuck doing things using existing paradigms. Which is fine if you're only developing an application, but not fine if you want to effect more fundamental change.
> Never start coding (making a solution) unless you fully understand the problem
Sometimes, I cannot understand the problem until I've started coding.
> Tech debt is like fast food.
Yes, in the sense that where I work, many people have it every day :-(
> go for this priority: Security > Reliability > Usability etc.
I don't buy that gradation.
> Don’t use dependencies
Contradicts a bunch of other points IMHO.
> Any function that’s not pure should be a class.
No. Verbs are not nouns. But - if he means functions with static variables, then maybe.
> Software is more fun when it’s made together. Build a sustainable community.
Very difficult in my experience. In a commercial setting, the company controls your software, and it's a totalitarian 'community', if at all. With your pet projects, it's often difficult to attract users to participate more actively.
(Forgive the lack of specificities, I tell this as a parable)
A fancy new office tower was opening downtown. Around this time, office space was at a premium so they soon sold out all their floorspace, and the project was considered quite successful except for one small problem; the architect seems to have installed slow elevators.
Tenants began to complain that the ride up to their high, and very expensive, offices was taking too long. These elevators were stupidly slow.
The building management frantically called around the big elevator companies, getting motors upgraded as fast as possible, but the complaints kept coming and in the end no one could quote anything less than $100mil to structurally alter the building to house bigger motors and more shafts.
Then one day an independent construction contractor showed up who offered to do a retrofit to fix the issue for only $10mil. The desperate building management decided to try it.
So the guy took $500k of the money to buy and install a bunch of mirrors in the elevator booths which had been sombre (and expensively) wood-panelled. Sure enough, the complaints stopped coming. (And the contractor got a $9.5mil payday)
The problem wasn’t that the elevators were slow. It’s that people got bored in them (in a world before smartphones). And what do people never get bored of doing? Looking at themselves in the mirror.
Of course, it’s annoying to save the equivalent of 95% of the cost but not see any of that money. You just had to deal with budget cuts that really should have killed any solution. Still a cool party trick I guess.
(presumably the lift has a "progress bar" equivalent in the form of the level indicator lights)
> Bugs’ genitals are called copy & paste. That’s how they reproduce. Always read what you copy, always audit what you import. Bugs take shelter in complexity. “Magic” is fine in my dependency but not in my code.
I definitely haven't been following this... anybody else using nearly 99% of functions in their frontend work? (Typically I'm in the React TypeScript world)
Any function that’s not pure should be a ~class~ component.
ie, when you have a bunch of functions that operate on the same kind of struct/set of state, and in fact you want to prevent other functions from messing with it (you want to make it internal), you group them in some way. The OO word for that is a class. React is a component; which are much more composable because we ditched the stricter concept of inheritance of OO classes.
Hell, the es6 class system is really just syntactic sugar around Brendan Eich’s nifty prototypal system. If it weren’t for the weak typing and lack of std.lib, JavaScript would be an amazing language.
…which is to say it would probably be Go haha
Over the past few years, at least on the frontend, I've found in every case what is a class could be expressed in a function or variety of functions. So yeah, still unsure about this point, maybe it was more for other software domains (backend, embedded, etc)
Funny too about what you say about 'grouping functions in some way'... I just made a post about how it makes a lot of sense to put a single function per file... with my organizing, a named folder is the only thing that 'groups' my functions together... so far this style of organization is working well for us.
https://chrisfrewin.medium.com/advanced-code-organization-pa...
It's amazing how this POV lives on, even in an essay which decries technical debt. Documentation and well-written code fill different roles with only some overlap. Your code can be beautifully written, but if I need to read the whole 500+ lines to figure out what you're doing, rather than read a single paragraph at the top of the file, you've burned through a lot of my time. Good documentation explains the why, code explains the how.
I cannot disagree more. Code lingers. I work on an 13 years old code base that is considered new by internal standards and it's not uncommon to see lines that haven't been touched for 10+ years.
I disagree with this. Dependencies become your code and any magic will always come and bite you, no matter if it's written by you or a downstream dependency.
The guiding principle should be: Reject magic at all cost
1. take an extremely impractical amount of time reinventing the wheel in house, or
2. take a (IMO a relatively much smaller) hit when you fail to take the 10 minutes to read the breaking changes on the dependency. Read this, make fixes, and everything is working again. If you hit a major issue like this with your own tooling, you're at the mercy of your team's ability to quickly build a fix.
A refined version of this point may be something like "only use dependencies which are very well tested, documented, list known issues, and have an active community of development and releases" - don't just install any package or library because "it appears to solve my problem"
At the time it was early days for personal computers (1991) and the doctors in our organisation were very resistant so the clinical booking system became a board with cards in it.
These days people are much more used to computers, and have their own, but there is an underlying principal, closely related to KISS.
Software will not eat the world. If it can be achieved without software it will likely (not certainly) cheaper and more reliable.
An example? Controlling a machine. Hydraulic systems are well understood, very effective. Putting a computer into the system generally decreases reliability for very little benefit.
And has crashed quite a few aeroplanes.
Every one of these is covered by lessons that OP didn’t have to learn themselves if they had just paid attention to the guidance available from luminaries in the field.
This leads me to the one lesson I’d give instead; let other people do your work for you, whenever you can. #13 on this list is far too strict, you’ll spend your days maintaining commodity code and discovering for yourself all the pitfalls someone else could have just solved for you.
If you’re looking for the definitive version of this, definitely check out The Pragmatic Programmer.
I always have had problems communicating this to my colleagues, managers and stakeholders.
I remember having this discussion with a stakeholder once when I was a consultant:
PM: "How long would it take to have a page with a table with this payment data and this design?"
Me: "The page you're looking for doesn't exist exactly, it's more complex than that."
PM: "What do you mean?"
Me: "There is a page that maybe, eventually will have this table".
PM: "But I want this"
Me: "Okay, but when the user lands on the page, there is still no data, it is being fetched from our backend service. We need to give the user some feedback and tell him his data is loading".
PM: "Okay, add a spinner or something".
Me: "But that's not enough. What if the server hasn't answered after 5 or 10 or 60 seconds? How would the user react? Should we retry the call? Should we give him some other feedback? What if the response actually contains no data, because for the given filters there are no payments? What if the data is malformed? This may crash the application. What if the user is not authenticated or not authorized for the data? Isn't this an admin-only route? Estimating the table itself is one job, but defining all the requirements and handling all the non-happy-cases or edge cases is a totally different game".
Problem is, explaining this to stakeholders, and sometimes even colleagues that "don't care and just want the task to be over" isn't always simple and the solution is generally "we will fix backend so it sends you always correct data in acceptable times".
And then, you spend much more time fixing these edge cases after you go in prod and it becomes a giant spaghetti ball.
A seasoned and experienced backender very well understands that a (well formed) 200 is just one of possible answers he's gonna give back based on the request and database data, and yet we keep treating the less probable outcome as the only scenario.
An assert that doesn’t quite make sense. An argument type that feels too specific. A hang in ssh once in a while.
7 times out of 11 they’ll turn out later to be the first sign of an error you wish you’d looked into sooner. I guarantee it, 82%.
I've often thought about what interesting findings you would get back if time and people were infinite and all those little things you thought, 'huh? thats weird' and put it in a queue somewhere and a few days later it had been thoroughly investigated and findings documented by some smart person. I'd expect you'd find some weird networking problems for why ssh hangs sometimes, misconfigurations, waste, security vulns etc. So many things we skip over day to day bc it would be a distraction.
I strongly don’t agree with this one. In my opinion, a well rounded individual will have hobbies and a life outside of programming that won’t leave time for pet projects.
Having said that, I do have a pet project that I’ve been “working on” for about 15 years. Maybe I’ll find some time to make some progress on it after I retire.
Rule #1: Software is a social service. If you're not writing for the user, but rather for the sake of writing code, you're not a good programmer. Write for the user, whether that user is Joe Sixpack or Jane Hotdev, or even if you are the user - assume the role of servicing the end user. Software is a social service.
But software specifically is social, because software is useless without a user.
I feel like he "gets it."
#10 is my fave:
> When making decisions about the solution all things equal, go for this priority: Security > Reliability > Usability (Accessibility & UX) > Maintainability > Simplicity (Developer experience/DX) > Brevity (code length) > Finance > Performance But don’t follow that blindly because it is dependent on the nature of the product. Like any career, the more experience you earn, the more you can find the right balance for each given situation. For example, when designing a game engine, performance has the highest priority, but when creating a banking app, security is the most important factor.
I would add "Localizability," just before "Usability."
In my experience, security and localizability need to be integrated from the very first line of code. They can't be added after the fact.
I hasten to add, I am not in that minority!
Now that I’m cognizant of this I first thoroughly review other implementations to try and understand why decisions were made, ultimately saving me from repeating the same mistakes.
Inconsistent documentation is also a problem.
I implement this by reading industry news, “spiking” projects for interesting frameworks, ides, etc. I find it helps to go broad in learning- learn a little about lots of things, but then deep dive when interest or need arises.
Amen, preach it from the mountain.
Can't say I agree performance is bottom of the priority pile. As someone who does numerical work semi regularly, performance can radically change the usefulness of software (interactive versus batch for example).
Code is permanent. Don't rush it. If it takes you 1.5x the time that it should to complete a feature, that is a million times better than rushing something out that is broken. Even if people are waiting desperately and it's a blocking issue. Take your time, get it right once, and move on.
"The only way to go far is to go right".
In other words: I'd be wary of sticking to these unless they fit who you are and the conditions in which you operate at your best.
What you do should be based on that analysis. If not, frustration, dissapointment and conflict (internal, external) will follow.
> Don’t attach your identity to your code
Others in your team will. Example here is a craftsman taking pride in their work. Think about that the next time you are reviewing someone elses work.
> Security > Reliability > Usability (Accessibility & UX) > Maintainability > Simplicity (Developer experience/DX) > Brevity (code length) > Finance > Performance
Performance is last ? Code length is more important than performance? That's wrong. Also correctness is not mentioned at all surely that has to be in that list.
> Don’t use dependencies unless the cost of importing, maintaining, dealing with their edge cases/bugs and refactoring when they don’t satisfy the needs is significantly less than the code that you own.
https://en.wikipedia.org/wiki/Not_invented_here
It's impossible to determine the cost of using a lib using these metrics. I bet this person has had a problem with a dep a few years ago that caused a lot of emotional damage and the response is to never use 3rd party deps where possible. That's not a professional response to what happened it that's the case I'd hope most Staff Engineers don't have this attitude.
> Good code doesn’t need documentation
This is impractical at the end of the day you'll need to have something for the user to lookup for reference.
> Never start coding (making a solution) unless you fully understand the problem
Most devs don't get to decide this they will be told to do X by someone in a large org and given a brief description of why. Refusing to do your work until you fully understand a problem will lead to dismissal.
Some good stuff in there but also some problems.
>Performance is last ? Code length is more important than performance? That's wrong. Also correctness is not mentioned at all surely that has to be in that list.
Depends on the scenario. Early in my career I was maintaining a scheduling DSS. You would enter in some appointments and it would consider a bunch of rules the users would create and find an appointment with matched resources. It was very cool and I really got into it. One of our test cases was scheduling 12 related appointments all at once. After some tuning, our system could do it in about 10 seconds, give or take. I decided to give a prototype a whirl with different ways to store and process everything (in memory) and got it to schedule these 12 related appointments in under a second. I showed my boss and he was impressed, but ultimately said, "the user's don't care about the difference between 10 seconds and 1 second, it's 1000x faster than doing it on paper." When the OP says performance last, I feel this is what he means. Some developers tend to tunnel vision on picking the best set of algorithm and caching mechanisms above all else, allowing the product to face delays when no-one cares about the difference between 10 seconds and 1 second. Of course, this is dependent on the solution. If you are google or Netflix trying to serve millions of users, performance matters. Most of the time, great is the enemy of good in this regard, especially in enterprise apps. Also, you can always tune trouble spots later down the road.
>I bet this person has had a problem with a dep a few years ago that caused a lot of emotional damage and the response is to never use 3rd party deps where possible.
I write stuff in house whenever possible. I've been burned by external libs plenty of times, and there is one I have in my current codebase I wish I didn't. It depends on the quality of your developers, for really good teams, this isn't a big deal at all. I use third party stuff for more specialized libs like image conversion and whatnot. The benefits of in house libs is they only do what you need, so they are tight and when something goes sideways, a smaller codebase trying to satisfy 1 use case is easier to understand than a large lib trying to satisfy 10 difference use cases. Plus, you don't always have access to the source code of third party libs.
>> Good code doesn’t need documentation >This is impractical at the end of the day you'll need to have something for the user to lookup for reference.
Yes, I'm torn on this. I've written plenty of documentation that nobody has ever read but me though. If he means "code with good comments," I'm ok with that. External documentation outside of good tight specs is hit or miss. A lot of it is just check boxing.
>Most devs don't get to decide this they will be told to do X by someone in a large org and given a brief description of why. Refusing to do your work until you fully understand a problem will lead to dismissal.
I don't disagree this happens, but if it does, you're probably in an environment that doesn't respect you, your work or your growth in the company. Get out ASAP. I always explain to my devs why I'm doing something; mainly so they won't write some funky code because they don't understand the problem.
> our system could do it in about 10 seconds, give or take
> user's don't care about the difference between 10 seconds and 1 second
They d,o but your boss doesn't. That is until they complain. Happened to me multiple times over the past 16 years. No one cares about Performance until it's too bad then suddenly it a huge problem. I've been in several projects where fixing performance was a rewrite due to this attitude. Performance should be much higher on the list period. Also not having correctness is insane.
> I've been burned by external libs plenty of times
Yes and you're not the only one but the "I've been burned therefore noone can use 3rd party libs" is childish.
> A lot of it is just check boxing.
That's presumably coming from someone who has a lot of experience and is very familiar with the domain. For those who are new to the company and new to working in tech in general good documentation is a godsend.
> Get out ASAP
Ah yes, heard this many times. It's completely impractical and often comes from people in an privileged position. Not all devs are paid high amounts and can quit their job on a whim.
You don't know. You don't work there. You're just making stuff up to bolster your position. Users never complained about the speed of the system based on that algorithm.
>Yes and you're not the only one but the "I've been burned therefore noone can use 3rd party libs" is childish.
No one said that but you.
>is childish
If you can't attack the position, attack the person, right?
Well we'll just have to agree to disagree. I'll run my team how I see fit and you run your team how you see fit.
I have a rule i derive from that: like economy of movement there must be a requirement for every change/code.
when nobody says what is slow, how can I optimize. I would pick simplicity and maintainability every time over some undefined performance. Especially as high performant solutions tend to be rather complicated compared to simple ones...
3rd party libs come at a price that is often overlooked/ignored...
Here's the full quote from the article: "Good code doesn’t need documentation, great code is well documented so that anyone who hasn’t been part of the evolution, trial & error process and requirements that led to the current status can be productive with it. An undocumented feature is a non-existing feature. A non-existing feature shouldn’t have code." Maybe you're just objecting to the first part, but he isn't saying "don't document".
I'm biased, but C (client) and Java (server) will never have substitutes.
The reason I can be absolute is that we have run out of energy.
Security > Maintainability > (...)
Experience is merely the prerequisite for skill, it doesn't cause it.
Man i still remember when it felt like a new js framework was coming out every day