Throwaway Code
blog.bencope.land
blog.bencope.land
It also reduces cognitive overhead. A lot of people seem to spend a Lot of energy thinking about whether a project needs proper coding or not.
No need to separate it: we just need to keep the minimal good habits all the time!
In an IDE I can quickly type out a full function body with 1-character variable names, then run it and see what happens. Immediately after I have seen it work I can go back and rename all of the variable names into something more descriptive. This is also easier now because you know exactly what the variables are used for.
You can do the same immediately for constants (most of the IDEs will allow you to "extract the given expression as a constant on the module level" or alike).
This can never work in a text editor because you need to know how that specific 1-letter tokens.
There is no reason to write non-descriptive code unless you're trying to save maybe 5% of your time. At a job where timelines can easily go orders of magnitude higher than expected, you're trying to shave off a minor portion of your net time spend...
Maybe you save a bit of time during very initial prototyping, when you're just trying to create the exact algorithm for something (eg working with a dozen or so lines of code).
As soon as it's actually used, even if in a "proof-of-concept" prototype, you'll easily spend double that time on cognitive overhead (eg, the next day/week, trying to remember what that variable is) or just in typing out comments. If other people are ever looking at or using the code, it's easily 10x the work you're creating.
Another way of saying this: once your code exists for more than a couple hours, it should have good (not necessarily perfect) variable/function/token names. I agree on the ide vs text editor comment, and add: another nice thing about using a good ide is you can rename the declaration and let the ide update everything else.
I can't write an algorithm without coming up with decently descriptive names for the components. Of course, sometimes I come up with better ones later, but the entire idea of using placeholders is deeply foreign to how I think.
Which isn't to say I don't use single-letter variables, `d` is a perfectly cromulent way to spell delta although `Δ` is better if it's allowed. It's uncommon, and when I do, they're no more likely to change than any other name.
Good enough naming can be tremendously easy, sloppy even, once you've learned which names don't pass that bar. Where naming does get hard, and where inconsistencies become painful, is when you have too many things that could have the same name.
In the wrong context `Foo foo` can be confusing though -- at best this leads to someone reading the code just spending more time, and at worst, it leads to someone modifying the code adding bugs. Just calling it something like `Foo currentUserFoo` is often enough to avoid this, and really doesn't take more than a couple extra seconds to write.
Still there is a huge opportunity cost if getting the wrong code up to production quality prevents working on the next prototype that gets you closer to the right thing. And I’m not talking about best coding practices, but putting in extensive comments, doing a bunch of tests, etc...
I also tend to wonder how often research results are tainted by subtle implementation bugs that may have been more obvious with better coding habits.
A good abstraction requires knowing how you'll be expanding the code (e.g. will there be more images in the future? Hundreds or billions? Or will that feature Y need to be built out?). Building that abstraction right away can definitely slow you down, as you'll be fighting it a lot of the time.
That's not the goal. You want code that can be refactored with reasonable effort.
For example if the number 3 signifies some factor that's used several times over the the code, make it a constant with a descriptive name. This makes even the prototyping easier and whoever takes over the code has a much easier life.
Or don't copy the same 5 lines all over the code with some minor changes but make it a function.
This is really not hard and has huge payoff if you don't overthink it.
And no, the goal isn’t always (or even often) to produce code that can be easily refactored, quite the opposite in practice. This is why having prototypes done in a separate org can be really useful (since they are writing code to serve designer needs rather than production developer needs).
IMHO, this is far better than starting from scratch. Both code and SQL are meant to refactored. Don’t over-engineer from the beginning adding usecases we know nothing about.
In the big, the n-th rewrite will be just as constrained by how much we can keep in our head at the same time as the original attempt. Refactoring essentially means offloading to the computer what you already have in a computer-readable format. What an opportunity!
What I mean with rewriting in the small is taking an existing logic-wart and, while trying to decipher it, taking notes in the form of a rewrite. After finishing the first approximation you take a long, skeptical look at both versions, "is that simple, elegant thing really doing the same?" and then you see the additional corner cases (and/or bugs, sometimes you'll never know) in a clarity that would be impossible to achieve just looking at the original code. It's mostly a reading tool. Sometimes I will trash the rewrite, but often the new version, with the non-obvious bits added will be far more readable than the original because it was written from the perspective of a reader with little knowledge of what the code should do, not by someone deeply immersed in the problem space.
Before this all came to a head, I started doing proof of concept work as command line only. This allowed me to give them estimates they had to listen to, instead of them just assuming that because they could see a UI that we could just pretty it up and ship it next month. Clearly, they reasoned (as if anything they did resembled reason) my two to three month estimate was sandbagging and it was okay to tell the customer we’re almost done.
I got to hear it from the coworker who was best at deadpan jokes, so I didn't believe him the first time when I asked, "Did I miss anything?"
I couldn't agree more with this. Not that long ago I inherited a project which on the surface looked shiny and nice. I was however aware that it was a mess underneath. Mind you, I was completely unprepared for the utter crap that awaited me. In all fairness it is the worst thing I have ever seen by light years. It took me two almost 2 weeks to find where a bug was coming from (it was written in a way which no debugger could trace it back, no logs, everything was surprised). After fixing it eventually, I decided to scrap it and build the whole thing from scratch. At the end it took me just over a month (Saturdays and Sundays included) and another 2 weeks from another developer to get a replacement project from an empty file. And here are the two painful truths about the whole endeavour: it took us collectively under two months to build, document, test, and run it in production. A grand total of around 20-30k lines of code. It took the people before us 3 and a half years to make their version which was anti patterns, top to bottom and in those 3 years the company invested around half a million Euros in salaries alone. If argue that this is the most painful thought. Not even the fact that this has been my first weekend this year in which I'm actually relaxing and not working. So my word of advice-if you end up in a similar situation, scrap everything, do it from scratch. Take a piece of paper, write down the business logic, draw a diagram of the structure of your application around it, pick the most appropriate stack and go for it. Avoid shiny new toys, profile everything and remember that sometimes it's better to let an application crash in order to find it's weaknesses.
We’re dealing with similar at work now. Over 5 years a system was built. It works and the code is crap.
Now I’m pushing management to please just decide “Yes, this is what we wanted all along” so we can throw out 90% of the code
Oh absolutely. Thing is, in my case, what was needed was clearly defined since the beginning. It's the "developers" who built it who were incompetent af.
The rule is situation dependant and some people can break it and succeed: the parent comment was very clearly one of those situations.
The actual risk is that the majority of us think we can break the rule, when it turns out we can’t. But a minority of people/situations can successfully do a complete rewrite, success defined as where the gains abundantly outweigh the pains.
There are times when rewrites are less total work, but they're hardly ever "easier" when you account for the cognitive load of making sure the requirements of the rewrite are being accurately captured. Maybe there's an exception if you know the requirements have been drastically simplified, but I doubt that's practically useful.
He was working on a service and managed to hack together some code that barely worked. His manager came around and a conversation like this unfolded.
Manager: How is your project going?
Engineer: Pretty well, I got something working.
Manager: Excellent! We have a meeting with a VP tomorrow and we want you to do a demo.
The lesson learned was to be careful and under optimistic about how you communicate to leadership.
I've seen people nail down the complete UI first only to spend umpteen sprint demos afterwards explaining why a "fully functional" UI can't be put into production yet to angry management. No matter how hard you try, you can't explain an iceberg to a management staff that wants the project to start earning money already.
> The lesson learned was to be careful and under optimistic about how you communicate to leadership.
Yes. Some people have to learn this the hard way. I would add, don't try and be sneaky either (ie: using different languages, etc.).
The secret is keeping it simple. Simple code can always be optimized later. It can always be optimized if it’s simple. Besides, the junior developers need something to bust their chops on anyway right?
Often times when someone takes over the project I’ve already listed in the library and in comments what can and should be improved.
I tend to use bottom up approach when I start by writing small bits and pieces of utility functions, tools, etc. and I build a larger proof of concept based on that.
Typically, the top level of the application is pretty dirty and changes constantly while I spend time to get the bottom of it correct right from the start.
I use the top level of the application as a kind of test framework for the bottom parts.
Then when the time is right, I throw away the top of the application and build the new one using some of the tools I have already developed.
I would love to do my throwaway code in Clojure but writing it in the same language as my production application lets me gain more knowledge, faster, and already have some stuff ready when I get to write the main part.
1) either you have the habit, energy and time to systematically rewrite your code properly through iteration;
2) or you are so good that your average throwaway code is considered not that bad.
Because mostly either we are going through well known paths which leads to case 2) or we’re exploring new things and being “skilled & experienced developers” we manage to have the right habits, energy and time to build things iteratively ending up in case 1).
If in the end we still have trashy code, then we need to ask ourselves if we are lacking any of the following: skills, energy, time or good habits (or a proper context, but that’s a whole other universe of problems).
I think maybe it’s similar to the way we misuse the term “crutch”. If you have a broken leg use the damn crutches. When your leg heals and you sell the crutches, don’t castigate yourself or anyone else about how useless crutches are. You used them. Past tense. That’s done now, and you’re moving on.
This helps me to validate ideas while keeping the main project in good shape. It is not as extreme as choosing a different language for the prototypes, but for me, it worked well so far.
I feel that this advice only fixes the symptoms and not the underlying root cause. This is a culture issue, not a technical issue.
Do some wire charts or graphics and present those. It's far more boring to do than writing a prototype, and learning design tools is cumbersome, but they allow you to demo things better to executives, redraw stuff on the fly in meetings, copy and paste to a document or slideshow, share with external parties, and people don't get hung up on minutae that come with presenting a prototype. Stakeholders far prefer seeing a well thought out document or slideshow than a dinky fake app.
Once you get signoff on the design and flow, then jump straight into the real code, skip the whole prototype step.
I'd rather be coding, but sometimes it's just not the most efficient use of my time.
Actually, hadn't thought much about it before, but being proficient at these two tools might be just as valuable to me as coding skills.
I have seen flavors of the latter work out, especially when you combine the idea of riskiest thing first (also last week) with reversible decisions and only decide today on things that have to be decided now. For things that are easy to change, make an arbitrary selection and keep moving. For everything else, play for time and hope that new information will improve those decisions.
At my last job, I prototyped half of the serious overhaul of a mid-sized product (backend and UI) in a bunch of ObservableHQ notebooks. It was all a pile of hacky JS, hastly-written over the course of a week, but it let me explore and showcase all the new product ideas and serve as a reference for my co-workers (and something to demo to the management), and at the same time there was no way it would ever end up in production. After all, it was all just a bunch of private notebooks on a third-party SaaS, and the presentation was explicitly following the notebook/document model instead of an application model. And, of course, our real backend wasn't written in JavaScript.
Once I know where I'm going I'll gradually refactor and spell things out to the level I'm aiming for, which is different depending on context.
Polishing code that should never go anywhere is a waste of life, and once you've spent all that effort it's more difficult to keep an open mind about the design.
But that's actually a fairly good idea (use a different language).