57 comments

[ 2.3 ms ] story [ 69.2 ms ] thread
Having seen too many times how throwaway code and proof of concepts went into production I try to always write decent code. You don’t need all the bells and whistles but just give things decent names, put repeated stuff into functions, avoid memory leaks, define constants and so on.

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.

I totally agree! Most of the time you just needed that little effort to keep things neatly and tightly together.

No need to separate it: we just need to keep the minimal good habits all the time!

That's one of the key reasons why I hate when people say they can "develop" in <insert your average text editor>.

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...

I'd questing even saving any time, let alone 5%. I guess the supposed time would be typing and thinking of a good name?

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.

Agreed, this is just one of those moments where I learn that other people have a very different workflow from mine.

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.

Do we even parse all the variable name when reading code? I know I do at least sometimes, because I know how often I am renaming in anger, but a lot of times I just see the IDE default of (in java syntax) "Foo foo; Bar bar;" and I'm perfectly happy with it. The tokens have a certain length that helps keeping them apart visually and they are not outright deceptive. Real life class names can be long, but the IDE even helps chopping them down reasonably.

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.

The whole point of naming things nicely is to reduce cognitive load. I don't see any issue with `Foo foo` because in the right context it serves that purpose, but by contrast, `Foo foo; Foo foo2` is basically always awful and is a case where it should be something more descriptive, like `Foo old; Foo new`.

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.

Think of programming code as communication to your future self and colleagues. Then we need less rules mindset, and more empathic one.
This is probably the best advice I’ve seen in this thread. Honestly, the pain of prototyping on bad code makes me try to write good code as early as I can in the process, to help my future self refine the code later (be it 10 minutes or 10 months “later”). I usually have a gut reaction to whether I like or dislike code, then try to identify why I have that feeling, and it’s usually based on how accurately code represents its own intent and purpose. When code does something besides what its name implies, I feel a tiny bit betrayed, and (if I have the time) I try to help it communicate better. This can be a module, a function, or even a variable.
I use a text editor rather than an IDE. I just write the full descriptive variable name straight off the bat. It probably takes <1 second to type it the first time, and my editor will autocomplete it for subsequent uses. I don't really understand the value of using 1 letter variable names.
If you work in a strictly prototyping or research org, then it is much easier to accept what you are doing is a demo/prototype, and since you aren’t part of a production org, their is little chance of your code going accidentally into production (eg for a UX prototyper/design technologist). These are only things at big companies, however, and even bigger companies often have developers pulling double duty.

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...

Personally I question the assumption that prototyping with bad code is faster than writing decent code. You don’t need full test coverage or deploy pipeline but I doubt that introducing constants, meaningful names, functions and other basic stuff slows down prototyping.
Yeah, lots of these things are just about cultivating good habits... Once it's a habit, there's no cognitive overhead.

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.

If your prototype only has a couple of buttons (or images you can select, or whatever) - it's faster to hardcore the names and handle those cases explicitly.

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.

Lack of abstraction for a one off (or two off) feature is rarely a problem. And if you need to do something 3 or more times, rhen you can probably build the abstraction pretty quickly...
Sure, but that isn’t nearly enough to make production worthy code. Also, naming conventions are both personal and organizational. If you are writing a prototype, you can use personally consistent standards, if you are writing production code, then you need to up that to an org standard (which you get from code review and such until it is just ingrained in your soul anyways).
" but that isn’t nearly enough to make production worthy code."

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.

Someone will surely overthink it, and everyone will always question you about where the line should be.

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).

Most people underestimate the value of refactoring existing code. Even a prototype provides a good base to start the refactoring. Of course it will help if the programming language helps with easy refactoring. In my projects, I expect to refactor the code at least 3 or 4 times before it reaches a state I am comfortable with.

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.

Well, you could also start from scratch multiple times and get better and faster each time, as you learn more things. And in the end you get a more fitting design for the final learnings.
If I had to rewrite something from scratch several times I would get very bored and the quality would probably go down. Better to refactor.
I like to refactor in the big and to rewrite in the small.

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.

Yeah, if it's a boring problem I would probably get bored too, but I don't think the quality would go down. Naturally, repeating things makes you better each time. Refactor has it's place as well, it's basically rewriting smaller parts in different parts to transform something, you're writing those parts again and again when you continue to refactor for years.
Absolutely agree. Refactoring is almost therapy. Clean up and fresh again
Refactoring production code can be very difficult. You often end up constrained by the existing code's public APIs, cross-API data structures, remote systems running the old code, etc. It's worth putting at least a little bit of thought into maintainability, even in the prototype stage.
Especially if it's a project with multiple people working on it. Refactoring will cause major merge problems for everyone doing anything else on those files. And if your team does best practices with code reviews and testing, then refactoring a chunk of code becomes a big project.
There was a time where this got so bad at one company, that I was getting neurotic. To put an order of magnitude on it, my mentor got fired for calling out his boss on some of the demo-ware shipping bullshit.

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 fear I have given a false impression by saying 'he was fired'. John, let's call him, was summarily fired. GTFO. And in an early morning meeting, no less.

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 think there are 2 cases in real world scenario:

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 write every line of code as if it were going out the door.

But that's actually a fairly good idea (use a different language).

> make sure the people using throwaway code understand that even though it kind of looks like it works, it cannot be maintained and must be rewritten

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.

It’s easier to build something once you know what you want than it is while you’re discovering what you want.

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

> It’s easier to build something once you know what you want than it is while you’re discovering what you want.

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.

In a perfect world, yes. In the case where someone decided to move around 12gb of data up and down 23 different classes just so they can count the objects in those 12 gb in every class, things start to change. In my case it was the latter.
“ Things You Should Never Do: They decided to rewrite the code from scratch.” This is a great general rule.

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.

My rule is be skeptical of anyone proposing a rewrite who doesn't fully understand the existing system, to such depth that they would feel comfortable refactoring it. Which means anytime someone says "we want to rewrite it because refactoring it is too hard" I tend to not believe them.

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.

That might work for video games, but in a web company I've seen so many throwaway bash, perl or python scripts wrapped in an `system(*command)` that are unmaintainable and sometimes cause RCE or injections bugs.

I feel that this advice only fixes the symptoms and not the underlying root cause. This is a culture issue, not a technical issue.

It's not a black/white thing, really. My code tends to be somewhat repetitive and terse while exploring new ideas.

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.

I find throwaway code to be freeing and fun. I don’t have to worry so much about making the perfect abstraction, or some DRYness optimizations. I’ve also had my share of throwing making it into production.

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.

A colleague told me an anecdote tangentially related.

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 X-Language shops write PoCs in different languages only to end up with those PoCs in production and a kludge layer between the PoC code and the normal X-language layer.

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 worst part about demo code like that is you have to code review every single line to make sure that the entire UI is using real data now and not some placeholders embedded in a function or the UI. We had someone go way down that rabbit hole once and I had to tell the manager that we don’t and can’t know how long it will take to ship this, because the entire thing is a fiction.
If the code is any good, it will be rewritten. Or it's perfect. Bad code however, no one want to touch it.
Similar to the infamous: "temporary" workarounds...
All of your code will be thrown away.
If your “throwaway” code works fine in production for years without any active maintenance and the only down side is that it needs a rewrite five years later, was it really that bad? Or is it the exact thing tech debt refers to - borrowing future time to work on what’s important? If I had to make a point it would be that there’s no line between code that needs to be replaced right now and code that is perfect and lasts for decades - it’s a spectrum and circumstances influence your tolerance.
The flip side of this is people who get sunk cost fallacy around that throwaway code. Rewriting a piece of code that’s been in use for two, three years doesn’t invalidate its existence. It wasn’t necessarily a mistake for it to be written that way. It is a mistake to keep on using it, though.

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.

There was an interview posted here last week about tech debt, and the speaker mentioned that if you compartmentalize throwaway code properly it can be kind of liberating. I don’t know that I’ve seen that in the wild, but it sounded plausible. Instead, he claimed, the only important part of that code - at all - were the interfaces. So save your design that part well and for the rest, save your energy.

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.

I use a different framework for throwaway code. For example, my current prod application I work on uses Apache Storm and Kafka Streams, my throwaway code is written with Eclipse Vert.x.

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.

I haven't written a prototype in forever. I don't find them effective. And they typically take way longer than you expect at the outset.

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.

Do you use any specific tooling to help with this?
My primary tools are actually Photoshop and PowerPoint. Lame I know, but they have served me well over the years.

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.

Krita's become mature over the years.
I like to have a folder called playground within my projects, where I build related prototypes and try out new ideas. Whenever something seems to work well, I do some sort of cherry-picking to keep the things I got right within the prototype and rewrite everything else.

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.

A related idea: use not just a different language, but a completely different toolset.

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.

I have the opposite problem - a lot of the code I think carefully about, factor correctly and ensure is thoroughly unit-tested spends about six months circling the plug-hole of tech demos and PoC before being sent to the bit-sewer and forgotten.