Hacker News new | past | comments | ask | show | jobs | submit login
Throw away your first draft of your code (ntietz.com)
288 points by herbertl on Aug 4, 2023 | hide | past | favorite | 223 comments



As an ML-focused python dev I have never been able to break the habit of REPL-driven development, but I find it works really well for "building code that works" rather than coming up with a tower of abstractions immediately. A typical python development workflow for me is:

* Start with a blank `main` file and proceed linearly down the page, executing as I go.

* Gradually pull out visually awkward chunks of code and put them into functions with no arguments at the top of the file.

* If I need to parameterize them, add those parameters as needed - don't guess at what I might want to change later.

* Embrace duplication - don't unnecessary add loops or abstractions.

* Once the file is ~500 LOC or becomes too dense, start to refactor a bit. Perhaps introduce some loops or some global variables.

* At all times, ensure the script is idempotent - just highlighting the entire page and spamming run should "do what I want" without causing trouble.

* Once the script is started to take shape, it can be time to bring some OO into it - perhaps there is an object or set of objects I want to pass around, I can make a class for that. Perhaps I can start to think about how to make the functionality more "generalized" and accessible to others via a package.

This is literally the only way I've ever found to be productive with green field development. If my first LOC has the word "class" or "def" in it - I am absolutely going to ripping my hair out 12 hours later, guaranteed.


> * Gradually pull out visually awkward chunks of code and put them into functions with no arguments at the top of the file.

I have seen scientific code written in this manner written in both Python and Fortran. This may be some intuitive way to start off, and even complete the task at hand.

But for people trying to read, understand, and realistically, debug your code, this complicates things.

Because each no-argument function can only work via its side effects. Your script becomes a succession of state transitions, and to understand it, you have to keep the intended state after each step in your head.

And in case of a mistake, you can't even call these functions individually via passing their intended arguments using the REPL. You have to set up all the state beforehand manually, call your no-arg function, observe the state afterwards. Which becomes more awkward the further down you are in your script, since all your dependencies are implicit and possibly even completely undocumented.


The correct way is:

1. Use comments to split visual awkward part into chunks.

# a lot of code...

## ===

# more a lot of code...

2. Use inner functions if that chunk need to be reused

3. Only move the chunk to top-level function if you think it's worth to take time to make its required state into parameters/return value


Sure that's like the worst case scenario, but in practice the entire point is that as soon as it becomes difficult to reason about, that's when you start cleaning up the functions and add some obvious OO. You don't leave it as a mess.


Agree 99% except this statement:

> Embrace duplication - don't unnecessary add loops or abstractions

I’ll usually make a function or perhaps tiny class as soon as I start reusing bits of code.

Apart from that, agree as stated. At my previous job (Python shop), a lot of the data engineers came from a Java background, and had a tendency to think top-down. Many things were over engineered ‘just because we might need it’:

- Factory classes used only once or twice in entire code base

- Lets make an AbstractReaderInterface because we might want to abstract the file type or location later (while 100% of files are Parquet on S3)

I’ve really enjoyed using dataclasses and Pydantics BaseModels prolifically, and adding type hints (coupled with type checks in CI).

Model the data, write a well structured imperative workflow, set up CI, write unit tests, enforce typing. Add OOP if needed, then close ticket.


Agreed. The general principle is (and it's a hard balance, of course, to avoid just slapdash work): don't do work up front you might not need. You will make your current work take longer than it should, which you will (correctly) be blamed for, and any time you save in the future you won't get the credit for.

This isn't a cynical statement; it's just my experience. Use it to your advantage!


I don't understand why you consider loops an abstraction. They are some of the most basic building block.


People are getting caught up on the loops thing. All I meant was in my line of work I often end up with many special cases of general processes. Writing a loop prematurely always bites me - I end up writing control flow for handling the one-offs, it somehow always becomes more obtuse than just listing things out literally.


Loop as an abstraction of copy-paste.


They considered it a dedupe not an abstraction

It would be unnecessary to have a loop over keys of a dictionary to call function xyz when you can just repeat the xyz calls (it would look nicer too)

Unless the dictionary is huge and dynamically loaded.


Loops are an abstraction over conditional branching and, depending on the kind of loop, some other things.


> Loops are an abstraction over conditional branching

How?


Goto. In the end it's all about manipulation of the instruction pointer.

Loops as deduplication is a very specific subset of looping that is very popular in some languages and almost nonexistent in others. If you don't have destructuring and convenient list literals you might never ever see it in the wild.

But even the Java 7 version (iterating an ImmutableList.of multi-nested Maps.immutableEntry of new FunctionN) can be workable despite its hilarious amount of repeated type annotations, if you have learned to stop worrying and love the tooling. Stuff like typescript makes it a breeze, so much that one might occasionally forget that it's not the regular form of looping.


What do you think is being evaluated on each iteration of a loop?


> Embrace duplication - don't unnecessary add loops or abstractions.

> Once the file is ~500 LOC or becomes too dense, start to refactor a bit. Perhaps introduce some loops or some global variables.

I agree with most, but if I "embrace duplication" I can reach 500+ LOC in half an afternoon :P. It seems to have really paid off for me to start some degree of abstraction (not OO yet in general) early enough. Tbf, it is easier for me to tidy up the code with some abstractions, rather than ensure that the "main"-titled script runs from beginning to end each point of time with no errors, which imo can hinder experimentation more than abstraction. But also depends on what I do, I guess, the greener the field the more I feel this way.


Wow, not a ML person, but controls and robotics. Yet, this describes my workflow for a lot of things almost to a tee. Even down to the avoiding loops. I tend to do that when I want to run the same simulation or analysis for a couple variables or datasets. It's interesting, because in the past I was a lot more prone to turn that into a loop early on. But this makes your code brittle. You'll want to do something slightly different for the two datasets, which means a bunch of conditionals in your loop. It's actually really similar to the problems you get with boolean flags when you try to abstract into a function too soon. It actually takes disciple to for me to commit to copy past but I think it pays it off.


I too agree with pretty much everything you say. Just want to add that I pretty much solely use ptpython[0]. It can handle line breaks in pasted code, vim (or emacs) bindings and syntax highlighting, and much more.

[0] https://github.com/prompt-toolkit/ptpython


Pretty much follow an identical process. When I do finally rewrite the code, after getting a working version, the duplication pretty much screams clean me up and simplify/generalize. I have never been able to just see the whole thing before I start. The process itself teaches you things.


Interesting thought (and coding) process. I love "design, thinkering and basically active-thinking" with pseudo-code in a txt file, i.e design.txt.

Just noting functions, d-structure and some flow usually helps to arrive at something worthwhile...


by REPL here you mean jupyter notebook?


I usually use VS code and the "interactive" python functionality (not jupyter). I highlight code and execute just that code with a hotkey. Works just as well with any kind of vim-slime like functionality.


REPL = Read-Eval-Print Loop. So could be iPython or just plain `python` in general, can’t say what OP is using of course


I also primarily write ML-focused Python. For me, having originally learned R and C at the same time, nothing has ever surpassed RStudio as a dev environment. For the past several years my preferred setup has been tmux and Vim with vim-slime in one pane and IPython in the other.

(Personally, and speaking only for myself, I hate Jupyter notebooks with a burning passion. I think they are one of the worst things ever to have happened to software development, and definitely the worst thing ever to have happened to ML/data science.)


Why do you hate Jupyter notebooks so much that it reaches “worst thing to ever have happened“ status?

Why do you love R Studio so much? (I’ve never used it, so no judgment)


> Why do you hate Jupyter notebooks so much that it reaches “worst thing to ever have happened“ status?

It's the "with a passion" part. A certain sub-population is prone to deciding that they love or hate something, based on some early experience or social context, and then every future experience with the thing is then strong-armed into supporting that supposed strong opinion. There is no rational reason for this. It's a very extreme form of confirmation bias.

It's pretty fascinating actually, as it's often times employed by rather intelligent people. With a slight tendency towards the autistic end of the spectrum, but there is certainly more research into this needed. Perhaps somebody working on a degree in sociology is interested i digging further?


I can't justify it - it's pure preference and opinion, irrationally held. A big part of it is probably that type of programming I generally need to do is closer to using an overgrown calculator (with DataFrames) than doing proper Software Development to build a thing.

I much prefer having the code over _here_, and then having the results in a separate pane over _there_. Jupyter style mixing of inputs and outputs tends to confuse me, and in my hands gets very messy very quickly.

The slides in this light hearted talk from JupyterCon in 2018 probably give a better explanation than I could.

https://conferences.oreilly.com/jupyter/jup-ny/public/schedu...


If like to know too. I learned python in jupyter notebooks. It makes experimenting and incremental development much easier (IMO) provided you remember to account for the current state of the notebook, which sometimes has me pulling my hair out.


> provided you remember to account for the current state of the notebook

Notebook style development is considered an anti pattern in most situations for this reason. It is too easy to execute out of order. Even the original parent of this thread said they recite the entire notebook every time to ensure they catch these issues. But, it’s not perfect and you can have leftover state this way too if you’re not careful.

My guess is that this is the reason the GP here is so against them. I find them helpful for data exploration, but that’s it.


Yeah I can't argue against that, I've been stung way to many times with it. Never heard of the term "anti pattern" before but yeah I get it.


Not OP but I often code in the REPL for python as well. Sometimes I'll stub out my code and just drop into an interactive debugger where I'm writing the next section.

In the python debugger, if you type `interact`, it'll give you the normal python repl. This combined with the `help` and `dirs` are super useful for learning new frameworks/libraries and coding with your actual data.


What interactive debugger IDE are you using that lets you enter “interact”?


pdb, the one shipped with python


Good idea to code in the debugger. Going to try that.


The temptation to throw away all of your code - be it a prototype or a "grown" code base - arises often.

Often it is a bad idea. I get it, though, there is an inherent attractiveness in the idea of starting fresh from a clean slate. Except, more likely than not, you'll soon find yourself in a similar situation to the one you started from. The fundamental problem is that it is easy to underestimate the edge cases. Sure, the main functionality is easily understood and straight-forward to conceptualize. But did you remember to think through all the smaller aspects of your software, too?

Perhaps it's easier with a prototype implementation that in fact doesn't have a lot of features yet, but to completely replicate the functionality of a complex piece of software isn't an easy undertaking. Sure, getting 80% there is probably easy, but the last 20% is the part that's easy to overlook when considering a complete rewrite.

Admittedly, there's nothing sexy about refactoring. And often it may seem like it's less work to just simply scrap everything and start over. However, that's fallacy a lot of times.


Throwing away an established code base is a completely different thing from throwing away a few-days-old prototype, and not what the article is suggesting. The points you mention don't apply to the prototype case.


This is such a HN phenomenon it infuriates me. Read article. Then read comment that misconstrues article to be something completely different, accompanied with loud critique. Read replies all in violent agreement to obviously self-evident strawman.


The misunderstanding you have is that often the article isn't the point. Just like a football match in a pub isn't always the point. People just want to chime in - the comment section is like an ephemeral pub.

If that bugs you because you consume information on the web rather than socialize, the solution is simple: don't read comment sections and make up your own mind about the article.


Sadly, it’s an everywhere phenomenon :(


Back in the day on /. they would just say RTFA and spare everyone the loquacious verbosity.


It is a something-man. Maybe a strawman? Or a weakman?


He-Man? Superman? Batman? Captain Planet… wait.


Yeah, this thread is a somewhat strange read. People don't seem to understand what "draft" or "prototype" means. Odd.


Just like management. I can't tell you how many times I've written a "Proof of Concept" that became production code.


I feel this. What I do now is increase the points of the PoC (real estimated PoC time + optimization).

I get the PoC down asap then as soon as it’s proven (privately to myself) I move onto optimizing. If it fails then I communicate early to discuss a change of scope.

Assuming it succeeds, by the time I present it I have already done significant work making it production ready. So to request an extra 10-25% time to “smooth out the edges” goes over easy after the demo.

In reality I end up with 50-75% time to get it prod ready. The rest is QA + prod bug fixes to take it over the (practically scoped) finish line.


To turn this around, a proof of concept is the fastest way to get code into prod ;)


I did that today. Rewrote a bash script in to a perl script as Proof of Concept. it's now in UAT... but I like perl, hey ho.


This has caused me so much grief. I find it key to communicate to all stakeholders the production process from PoC to MVP to MVP+.

Still managers always push against redoing work they can see working for the happy flow.


> Admittedly, there's nothing sexy about refactoring. And often it may seem like it's less work to just simply scrap everything and start over. However, that's fallacy a lot of times.

Agreed. Though sometimes you need to refactor the core of an application, in a way which will touch the entire app. To do that I often make a new, empty project. Then I rewrite the core of my project into the new folder (in whatever new structure I’m trying out). When that works, I slowly copy the content from the old project into the new project - refactoring and testing along the way.

But it’s not perfect. About half the time I do this, I discover halfway through that I didn’t understand some aspect of the system. My new design is wrong or useless and I throw away all the new code. Or I figure out that I can just make the changes in place in the old project folder after all, and I bail on the new code and go back to a traditional refactor.

But no matter what happens, I don’t think I’ve ever regretted a refactoring attempt like this. I always come away feeling like I’ve learned something. How much you’ve learned from a project is measured by the number of lines of code you threw away in the process.


I find refactoring sexy. Just saying.


> The temptation to throw away all of your code - be it a prototype or a "grown" code base - arises often.

I want an editor plugin which allows me to mark sections of code as reviewed or "perfect" (depending on how honest I'm being). Then, when I'm tempted to rewrite everything, I can go through and mark what I think is good, and then focus on refactoring the rest until I think it is good as well.

I'm tempted to rewrite code because I lose track of what it's doing, or I've learned a lot since I wrote that old code and so I'm not sure if the old code is good anymore. It's not so much about rewriting the code as an exercise in getting familiar with the code I've already written. I want a tool to help me with this.


I would think a combination of git, unit tests, and comments would solve this problem?

Unit tests prove the code works as intended, and are basically examples of what the code is doing. Whether the code is actually "good" is a bit more subjective -- but tests give you the freedom to modify it without breaking it.

Checking into git frequently is also a way to give yourself some freedom. Commit at every milestone, like every time the next thing is "working". If you feel like refactoring, go for it -- you can reset back to working state in a few seconds.

And lastly, leave comments in. You can always clean it up before you push. You can even squash or interactively rebase your history so no one else sees the gory details how the hot dog was actually made.


The theory and the practice of unit tests are often different things. In theory you’ll make changes to the code, and the unit tests will show whether they had the intended effects.

In practice, because often the unit tests are mocking out all the classes they interact with (as is the recommended style for isolated tests) you’ll either have all the tests explode constantly because the interface being mocked has changed, or you’ll have tests that confidently tell you things are working despite the methods they call no longer existing.


If you could modularize code in such a way the sizable chunks could “just sit still!” then entire modules could be marked as perfect. But you wouldn’t have to since you wouldn’t naturally need to touch them (e.g. how many codebases are using a fork of core-utils, for example, … to exaggerate a conways law effect).

But unfortunately the modules don’t make themselves apparent at the start of the project. So it needs refactoring discipline.


I don't really feel that attraction to complete rewriting. I wonder if the people who do are very smart, and thus able to hold more state in their head, so ugly code bothers them more even if it's not actively a problem, because they are able to have background tasks in their mind to worry about it?

And at the same time, perhaps their code is less encapsulated, because they didn't optimize for abstraction, they optimized for beauty. A leaky abstraction doesn't bother them, because ALL abstractions are leaky to them, they probably have a sense of internal workings even whem using household appliances, but ugly code tucked away somewhere bothers them a lot, and they might dislike using popular large libraries even if they work great, just because they're not comfortable using what they don't understand deeply.

My evidence of this is the fact that suckless exists and people actually use it, I assume their experience of thought is very different from anything I have experienced.


Speaking personally my urge to rewrite at least partially comes from not truly understanding the problem and the solutions to it until I've written something reasonably functional. It doesn't matter how much time I put into sitting and theorizing, there's always things I didn't anticipate and assumptions that turned out to be incorrect.

This usually means that rewrites are significant improvements across the board, especially if they're done a relatively short time after the original is finished since it's all still fresh in my head.

This may be a weakness of sorts on my part though, I lack formal engineering training which might be why purely mental modeling (no code) doesn't work all that well for me.


Yeah, I find that the desire to rewrite often comes from the final (working) solution having poor code ergonomics. It's not necessarily that it's wrong, it's that it's awkward or clumsy to understand/use, because my mental model of how it worked didn't take into account the actual practical day to day usage of the code.


I get around that by writing the API first, with usage code examples and all, Or in a GUI app, the UI first. I'll iterate there if needed, and only rewrite internals if it's truly bad or I think it will cause a problem later.

A lot of situations where the code is so nasty I actually have wanted to rewrite... I wind up just throwing it away instead.

Like, one of my happiest days in coding was throwing out some code to support JACK and going all in on Pipewire. Another happy day was replacing some DIY code with a GPL licensed library, deciding that I really didn't need the option to do proprietary stuff anywhere near as much as I wanted less thinking about gstreamer.

"If it's hard to explain, it's probably a bad idea".... If I try a bunch of API variations and I can't come up with anything that doesn't require learning 3 new algorithms to use... Maybe I'm not making a library or an abstraction, I'm making a proof of work hashing scheme that users have to do manually to access an encrypted version of the complexity I'm hiding, and I need to stop before I make z80 assembly in JSON to autogenerate Vue templates that render to cobol.


ALL abstractions are leaky - this is an objectively true statement.

As others said it's not for beauty, it to make sure if there is abstraction it fits the problem. If there is encapsulation it doesn't get in the way. For some coders they can get it on the first try and there is no reason for them to rewrite code. For the rest of us mid coders we need to explore first as well as make sure all cases we desire for Lib/API work.


You're right, I should have been more clear, all abstractions are leaky, but some are right enough most only notice occasionally.


Smart people can ignore ugly code. It's the people who get easily confused that need to see clean and easy to understand code.


Humans always make mistakes and are tired now and then, and the smartest people can still appreciate not needing to solve puzzles just to understand or debug their own code.

Of course clean and readable means very different things to different people, but I don't thing I ever regretted cleaning up code, while I can think of a lot of instances where not doing it wasted a lot of time.


If other people need to read your email/doc/code, then any effort to make it more readable will save them time, regardless of how smart they are.

"If I had more time, I would have written a shorter letter."


Sadly, I think this is true of me — at least of me. I don't think smart people (by this definition) are that common though, so clean code is a sensible default.


There is some point regarding changes on the code where rewriting is less costly or more costly than changing the current code. For me, a big part depends on whether I depend long term or short term on it and how deep I will have to go anyway using it.


I'd rewrite code to make it simpler and easier to hold in one's head, not just to make it pleasing from some aesthetic viewpoint.


It says to throw it away after a couple of DAYS, which seems to differ from other advice (like from joel-on-software)

Maybe this is ok, given that time period?

or will the rewrite have all these extra bells and whistles?

or will the rewrite throw away the unneeded bells and whistles?


Ok, perhaps don't throw it away. When I come up with an idea (for an app usually), I use "a few" A4-sheets (anything between 5 and 20), scribble and draw on them with a pencil, draw screens, buttons, data flows, activities, write notes (in various font sizes). Then I use my CamScanner and call this a v1.

Then email me the PDF and store the papers in a box, and a couple of days later I start the v2 (same process), then v3.

By v4 it's 'good'.

I also use the same method on my 9-5.

I take VERY seriously the Abraham Lincoln quote “Give me six hours to chop down a tree and I will spend the first four sharpening the axe” on almost everything.

I consider the v1, v2, v3 as the "sharpening the axe" and the v4 on the actual cutting.

In that spirit, the article has a similar approach, as the v1, v2, and v3 may take you down an (more than) imperfect path.


> Admittedly, there's nothing sexy about refactoring.

There's the "strangler fig" (my favorite) method of refactoring. It's where you rewrite just a small portion of the software, little bits at a time. Instead of doing a full rewrite.

IMHO, it's the best way to refactor. You can switch out both "old" and "new" versions at-will until you're 100% sure you've covered all the edge cases.


Game companies implicitly do this, throwing out old versions of their code (sort of). You make a game. Ship it. Start on a new game. Your previous game is now sort of a practice run for making a new game from scratch. Continue, ad infinitum.

It's weird: you rarely have to maintain something for 20+ years, and you get to always improve and iterate on how you did things last time. But, are you training yourself to write hard to maintain code, since you don't really have to maintain it past a certain period? Or does the learning-from-iteration actually make writing-maintainable-code easier?

I know some people do keep developing their games for decades, look at Dwarf Fortress, I'm just talking in general.


Minecraft is reaching that 15+ year mark


That's the most successful game of all time, hardly a typical case.


what you do you get good at. if you don't have to maintain there is no point in getting good at it or any reason to think you will gradually get better at it.


Required link to Joel on Software:

https://www.joelonsoftware.com/2000/04/06/things-you-should-...

Granted Joel is taking about a wholesale rewrite of proven / established code. Code and has had had the advantage of time.

Personally, I end up rewriting parts of my code often. It usually takes a mile for me to find exactly the right way it should be based on how are people use it.

You absolutely should rewrite prototypes, and re-factor important chunks of code. I rewrote something 3 times today, it was better each time.

At the same time you should be wary…


Of the reasons he says engineers want to rewrite the code, he says: badly organized code, slow processing time, and ugly code.

Maybe I’m not an engineer, because I bet perfectly pretty fast code is an indicator you should reorganize it to make it easier to adapt, add more capabilities even if it runs slow, and put some ugly fixes in there so it’s useful for the end user.

Agreed that wanting to over-engineer and perfect something that should be constantly evolving is not a good reason to rewrite it. That sounds like back office bubble pretend-work.


Ah yes, starting from a clean slate.

https://devrant.com/rants/816880/i-ve-done-it-again


Rather than "throw away" I would "start from scratch" meaning I can refer to prototype but reimplement with respect to appropriate norms. As a version control fanatic, very little gets really thrown away in my book. Just hidden.


Yeah normally when I do this, it’s because I picked bad abstractions. The main business logic I can bring over almost line by line, but I want to reshape the abstractions / data structures / interface to that logic


Nah. I think a prototype is something you definitionally should throw away. There's a huge freedom to knowing that instead of taking on tech debt, you're committed to declaring tech bankruptcy and throwing away the code. It lets you try things, cut corners, and generally experiment. It's great for thinking through what everybody really wants.

I do agree that it's generally a bad idea to just throw out a long-lived code base. But for me that's not an estimation issue. It's because the urge to throw it out is usually a response to upstream problems that haven't gotten fixed. For example, a lot of code bases are a mess due to time pressure. But if you don't fix the process problems that turn time pressure into mess, then you're just doing to end up with another mess. Possibly a bigger one, in that stopping all productive output in favor of a rewrite usually makes business stakeholders crazy, causing increased time pressure.


I've thrown away and redone a lot of code, both mine and others', and I've never regretted the time spent vs continuing to use the old thing.


A possible alternative is to use a different programming language for doing the prototyping than the one which will be used in production. The urge to hang on to prototype code can be removed, because you are going to do a rewrite in a different language, regardless.

By using 2 different languages, there is more freedom to just make a demo, discuss, and then decide what to keep or what direction to go.


RTFA


I've never respected this advice, even though I hear it often enough. The reasoning appears to come down to this:

> If you know that you're possibly keeping the code, you do things in a "proper" way, which means moving slower

combined with the idea that it's faster to redo work than it is to refactor.

I don't buy either of these propositions as a universal rule. For the first, it seems that a mindset of avoiding premature optimization, which you should be cultivating anyway, is enough to ward it off.

For the second, I see it as very context dependent, with my bias going towards refactoring instead of rewriting. I most often get things right enough the first time that it really only needs to be refactored going forward. Only when I end up with serious flaws do I decide to start over, often before I finish my implementation because the flaws are already apparent enough. Add to this that even very large changes can be refactored effectively and timely in my experience, and I feel like the bar for rewriting is really high to be cost-effective.

Unfortunately this essay and many others seem to take objections like mine for granted, and I should just believe the author that rewriting is faster with zero supporting evidence.


> I most often get things right enough the first time that it really only needs to be refactored going forward.

I think it really depends on your values and what you’re working on.

If you have the mindset of a product engineer, the thing you care the most about is how well the software works for your users. The code is an unfortunate necessity. When I build react apps, I tend to think like this. The code I write first go is often good enough to last until the next redesign of the UI.

By contrast, if you’re thinking of the code as a home you build for your work that you will live in, then having a tidy home becomes its own reward. Good decisions today (in terms of cleaning up or refactoring code) should lead to more velocity later. When I think about building a database, an OS kernel or a compiler, I think like this. When I start, I don’t know where my code should be rigid and where it should be flexible. My first guesses are usually very wrong.

Personally I prefer the latter kind of problem. I like it when there’s no obvious way to structure my code and I have to explore while I work. The code I’m most proud of writing has probably been rewritten 5 or more times before I landed on the “final” design. The “right” abstractions are rarely obvious at first glance and the search can be incredibly rewarding.

The rich history of application programming abstractions suggests it isn’t exempt from this either. It’s just, when you’re building a UI there have been an awful lot of explorers who came before you and can show you the way.


I agree that it depends on factors like what you describe which is why I am asking those giving the advice like this to consider the context at all. I also happen believe that the contextual bar should be placed high. I have to say, what comes to mind when I read your comment was "What about this does refactoring not accomplish?" In the metaphor of building a house, refactoring is what keeps it tidy and organized, and rewriting would only appear necessary in the case of your trash chute leading into a bathtub or some other critical design flaw that could only be addressed timely by redesigning the entire house.

If you are in a context where you are in danger of making critical design flaws often, or where circumstances make it hard to make changes after committing, then I absolutely agree that rewriting is effective. That's just what I mean by a high bar, one that I personally don't encounter very often in my work.


I definitely disagree with you on the first point.

Setting aside the part that's just fussbudgetry, I think "proper" coding is about doing things that pay off in the long term even if they feel burdensome now. As an example, I'm a huge fan of good automated tests for long-lived code bases. But if I'm just writing a quick, throwaway script, then building a bunch of automated tests are going to slow me down.

Throwaway prototypes are another area where you can save a lot of effort if you have really committed to throwing the code away once you've learned what you set out to learn. As a physical analogy, before the Long Now built out their bar/cafe space in San Francisco [1], we spent a day in the raw space building furniture and walls out of cardboard and tape [2]. It let us get a real sense of how the designs would work in practice for actual use of the space.

In the past for a number of projects I've done throwaway prototypes and then started fresh with "proper" engineering, which for me usually includes pairing, test-driven development, and continuous deployment. I've also done ones where we think we understand it enough and just start building. And either way, we usually end up evolving ways to prototype on top of the existing platform via feature flags, etc, so we can learn something from the real world and then decide whether or not we want to refactor to accommodate a major change.

[1] https://theinterval.org/

[2] https://vimeo.com/73959127


Using boxes is great! The beauty of coding is that those boxes don't need to be discarded, we have the virtual power to transmute them into real chairs and tables via the power of refactoring. It's like Photoshop vs. watercolor. I'm going to sketch either way, it's just a matter of what the bar is for throwing it all out, and I think it's very often sufficient to decide in the moment in a virtual environment. That's all to say, going back to my first point, I don't need to decide ahead of time whether the code will be kept, I'm going to keep it loose either way.

And there are situations I'd go in assuming I'll probably rewrite the work, but I wouldn't set the criteria so broadly as to cover literally whatever the next large project I embark on will be, as the article suggests.


If that works for you, great. But generally when I see people "keep it loose" on a prototype that then gets kept when some decision point is hit, they don't actually take the time to clean it up to production grade.

And personally, although I'm very good at refactoring, I will still go the path of a disposable prototype in the future when I think it's more effective in terms of total effort. Which for me is any time I expect the prototype to yield significant information about both the surface and the substance of what we're doing.


looking for gaping security holes is premature optimization. if you run into security problems down the line, use a security profiler and find the hot spots i.e. radioactive code


> looking for gaping security holes is premature optimization.

I won't approve a PR if I notice such a thing (and if it really is "gaping" I'd expect an automated tool to flag it and fail the build anyway). I don't really see ensuring your code follows basic secure SDLC best practices as "optimisation" at all - in fact it often comes at a (hopefully small) cost of less than optimal performance. Of course there may be areas of software development (hobby projects etc.) where security is not a priority at all, but if it's code you're being paid to write that runs on the cloud or your customer's machines then security is absolutely worth getting right from the get-go.


Usually I think it is unnecessary, but you might want to include the “/s” marker in this case. It is just close enough to believable that someone would think this, given the amount of insecure trash products that have been released…


I think this depends greatly on the product area and ease of updating. Removing problems categorically from designs early has been valuable to me in the past to reduce surface area, avoid impossible refactoring, and eliminate systemic bug classes.


I'm largely with you on this... do the simplest thing that gets a job done, that you assume will be replaced, and don't be surprised when it's still un use a decade or more later.


I like to build my first version in one file with as few abstractions as possible. No helper functions, unless absolutely necessary, just straight-forward code. Building good abstractions later on is a lot easier than starting with wrong ones in the first place.


This is an underrated way of doing things that I've come to appreciate over time. Heck, I'll even go live with the all-in-one-file thing until there's a reason to split it up. It's all peanuts anyway compared to bigger decisions like what API, DBMS, DB schema, other deps, libs, etc you use.


Definitely agree, if it works (tests prove it works) it can go live.


Very interesting approach. As someone who has overengineered last major project I worked on......maybe this is the way. I got so fixated on getting it "right" using current standards and abstractions that it became a bit of a mess. I despise working with that code base now.


I've always done it like this as well. Initially I did it for the most obvious reason: it gave me the working software that I needed the fastest. Then later on, I always found it to be the best way to find natural abstractions as you go.


I'm gonna second this. Just build it first and add the abstractions as they become obvious


I'd rather do the Ship of Theseus thing and just harden the prototype as required, and only when faced with real-world evidence.

The problem with rewriting is that sometimes you face second-system syndrome[1] where you overengineer everything.

Believe me--it happened to me at Groove Networks after Lotus Notes (ask your grandparents about that).

[1] https://en.wikipedia.org/wiki/Second-system_effect


Yet I find when rewriting from scratch there's a countering force of simplification where I find myself thinking "We spent way too much complexity on that part, and we can entirely do away with that other part, and we turned out never to use features X and Y."


> "...and we turned out never to use features X and Y."

THIS. Often, the best way to start the rewrite is to go straight to the database, and start asking "what allowed values for all these fields have never actually been used?".

EDIT: Whether it's rewriting, or refactoring, or adding some "if( Deprecated_Feature_Allow !== true ) ... ErrorDie( Feature_Deprecated )" logic here and there, or updating training material, or whatever - knowing that Software_Feature got ~zero real-world use is d*mned useful.


Agreed--every software engineer I know loves deleting useless code.

But this is not incompatible with refactoring instead of rewriting.


Yeah, that makes sense too.

I think it just boils down to "keep writing systems and you'll get better at it." But there's no bait on that kind of link.


I rewrote a mission critical application. The original app was something else. One function had 1500+ lines of code. It was the ugliest thing I have seen in my life. We wrote cleaner code in early college years. However, that code ran the business for 12 years with almost 0 modifications. People created work around for some stuff. Error handling was poor. But the goal was achieved and business continued to grow.

I think devs in general sweat too much about code quality.


Some disputes about quality are simply differences of opinion or style.

There's not really a book or standard on opinionated code quality that satisfies a majority of the people in the field. There's way too many ways to build things for that to exist.

This leaves it up to individual teams to approach a shared consensus (with some wiggle room) within their domain and tool set.

One thing that is universal is how strict the enforcement of a team's quality standards. So as a developer, you have to decide whether you like strict or lax enforcement of quality standards. Everyone can decide that but I think most don't.


> However, that code ran the business for 12 years with almost 0 modifications

We have code like that, and it requires everything around it to twist and contort to comply with the beast of unmaintainable at the center of it all, simply because at this point it has so poor test coverage and is so incredibly mission critical now.


Test coverage!? I don't think that word was in the vocabulary of the dev who wrote it :).


Code quality isnt that important if you check this code "once a year" and dont have time pressure to change somewhere in there. Or if you are seeing this code daily and know all the quirks.


> I think devs in general sweat too much about code quality

I think if they sweated a little more then poor quality code wouldn't find itself into the codebase.


Bad code doesn't usually come from lack of effort. It usually comes from business requirements that grow over time with deadlines attached.


It can still be a lack of effort put in trying to convince product managers etc that more time is needed!

But almost all PRs I review have something in them that suggests the developer not putting as much thought/care into what they were doing as I'd like to see (and I'm just as capable of doing the same).


Decades ago I spent a whole day adding a serious new feature to my software. Had the first version all polished up and ready, and through a misunderstanding with my version control software (probably RCS, maybe CVS, probably not SCCS), I lost that work. Like maybe I did a checkout instead of checkin, something like that. I went to bed quite annoyed.

The next day I got up and started from scratch. The new implementation took me half a day, and was decidedly a better implementation.

Fred Brooks turned out to be right.


This would sound like questionable advice except that the best code I wrote in college resulted from an accident…

I’d sat down all night and written some C for a graphics class. Everything was great, yay, victory. Proceeded to tar it up per class guidelines and did something like this:

tar cvf assignment.c assignment.h data.txt readme whatever.cuz

And as many of you no doubt recognize, I’d forgotten to supply a destination name before listing the source files.

So the code was gone. My brain was gone, too, so I explained what happened and sent what I had, including a working executable, to the TA. He gave me an extra day to finish.

So the next day I sat down and wrote it all again and as mentioned above, it was some of the best code I’d written. Doing it again from the top saved all sorts of dead ends and mistaken approaches.


This is exactly the type of story I tell when people are anti pair programming. I ask them if they’ve ever lost a days code (usually asking if they ever used source safe) and that when they finally knuckled down to write it again, it took about 20 minutes. As the writing the code wasn’t really the hard part or the bottleneck, solving the problem was the hard part. Hence pair programming having two heads on one problem to solve it better/faster, find the edge cases etc.

Out of curiosity, was it really fast for you to do it in that extra day or did it take all day?


Much faster the second time. Like you said, all of the problems came pre-solved. :-)


The idea of prototyping to uncover 'unknown unknowns' resonates with me, it's like a reconnaissance mission before the actual project. This could indeed save a lot of time and effort in the long run...


Just never let management see the prototype or they'll consider it done and tell you to move on to something else.


In my experience, this is what actually happens. A developer makes a low-quality, low-effort prototype under the assumption it won’t ship. Someone sees it. It gets shipped. Everyone loses.


Sabotage your prototypes then. Have it reset its state every 5 minutes or deliberately leak memory.


Sneaky move.... but then, in the eyes of some people, you might well appear not competent to put together a working system, so make sure you know your audience.


Ya, this basically.

It helps if you have tests because when prototyping, you can write things in a way which break tests and ideally break other features. It works enough that you can validate the one idea. That way it will never pass CI.


They are cunning. You cannot sabotage it enough to fool them.


Reconnaissance mission is a great analogy. I think of it as scouting from the Age of Empires.

Besides discovering unknown-unknowns it also helps in conveying design/idea to rest of the team.


Another war analogy was the Byzantine defense. Their policy was to avoid decisive, large-scale battles where a lot can go wrong at once. If an enemy army is incurring, first try to weaken or deter it with lighter forces that also gather intel. If they can't stop the threat, form and advance a larger army with a better-planned supply chain to stop it deeper in the territory.

Which actually doesn't work so well in AoE. The game doesn't have a concept of army supplies, so you usually just want a huge unstoppable army you can send anywhere.


"tracer bullet" that shoots through all key parts of the system (agile terminology, I think).


Throw away 1st, second, third, etc... Write your code in small replaceable/disposable POCs until at some point the architecture and coherence starts to take shape.

That's the advantage of software over other work. You have 0 material sunk costs. Unlike building a bridge, which you can't just tear down and iterate, with software you can develop a workflow where you break things and iterate quickly.


There are of course software systems with a very large bit of complexity and entrenchment... least of which the complexity added in order to avoid said entrenchment in the first place.


I have found that this kind of advice does not play well with managers (but it's actually not a bad thing to do).

I'm fairly grateful that I've retired from the rodent rally, and am working at pretty much my own pace and structure.

About six months ago, I tossed out the code for the frontend of a project that I'd been developing for over a year and a half (at that point). I distilled the business logic into an SDK, brought in a designer, and started over from scratch.

The result is ten thousand times better than what we had (and were considering shipping), a few months ago.


The funny thing is is that this is part of TDD (and extreme programming) that most detractors of TDD don't understand. I'm talking about the old "but if I don't know what I'm writing, how do I write a test first?" crowd. Well, the answer is this article. Write a quick prototype to answer unknowns, figure out a general plan, then throw it away and start with a single test. I emphasize "single" because there was that other article that made the front page a couple of months ago where the author described their "new found way" of doing TDD that made them actually enjoy it and then went on to describe regular plain ol' TDD.


The famous comedian John Cleese speaks about this where he loses a script and has to rewrite it from memory before a deadline. Then he finds the old one and finds out his new one is much better despite thinking it was the same. So he started using it as a technique for better script writing. It is referenced here but I can’t find the video https://www.ideaconnection.com/right-brain-workouts/00186-jo...


I don't know why, but calling him a comedian sounds like an insulting put-down. He is a Python! (Maybe because, in this context, python excels at rapid prototyping?)


Plan to throw one away. You will anyway. - Fred Brooks, The Mythical Man Month.


Personally, I would never or rarely use the approach where some code is created and then thrown away. I think it is always better to first think and then do.

With software development, I make the distinction between a functional analysis and a technical analysis. Functional is clarifying the actual business requirements. Technical is clarifying how to implement the new functionality into the system.

During the technical analysis, you are supposed to check and update existing class diagrams, existing api specifications, and so on. Depending on the experience of the team you need to describe this in more or less detail.

The technical analysis must be done by someone who has knowledge of the project. That someone should be aware of the quality of the data in the database, or (in case it is relevant) should explore this while doing the technical analysis. It would also be normal to check the source code while doing a technical analysis to see how things are currently working/built and where the new functionality can fit in.

The reason for investing time in a technical analysis, is to start the implementation with much higher confidence and get a decent implementation from the first attempt.

You must find a balance between the time you spend on technical analysis (without writing any code) and the time you spend on the implementation itself. Spending more time on the technical analysis should save more time on the implementation, but I am aware (and convinced) that some problems will only be found by creating the actual implementation. However, I am also convinced that these problems rarely require a fundamental change in the implementation that a rewrite is needed.

In short, when thinking before implementing, the quality of the first implementation is high enough.

Also, what is not clear to me from the article. The author mentions to have 1-2 highly skilled engineers create a first implementation. And the author likes to be one of those engineers. These engineers create a prototype and then throw it away. The engineer who created the prototype now has an idea how the code must be implemented. How is that information communicated to the (lesser skilled?) engineer who must then implement the new feature?


I've felt this, and I agree. It really pays of to do some work on technical discovery, make all the mistakes, see where all the design issues will be, validate as much as possible, and then start again. And the best way to really scope out requirements and get to know the "unknowns" is really to get your hands dirty.

Especially when in comes to business logic and domain modelling, some bad decisions at the begining can cost a LOT, to undo in an iterative matter.

(To clarify I'm talking about the timeline the author proposes of "a couple of days" build the throw away prototype)


I think sometimes it's a good idea to use a language the company doesn't use internally to prototype. Like we don't use python or node in production, but a lot of stuff can be POCed in those languages/frameworks without a learning curve.

It forces you to throw out the old code and forces you to think the problem out in different ways.

Also, in my experience the POC does enough that it turns into just use POC because of the illusion that it would be faster to use than to rewrite. Then it's a span of time fighting edge cases and added features thought of afterwords till it passes all testing. Then you have a battle tested piece of code you can't get rid of, difficult to understand, and ridged for changes.


Jon Bentley's classic column "Bumper sticker Computer Science" includes the following bumper stickers:

[Brook's Law of Prototypes] Plan to throw one away, you will anyhow. Fred Brooks, University of North Carolina

If you plan to throw one away, you will throw away two. Craig Zerouni, Computer FX Ltd., London, England

Prototyping cuts the work to produce a system by 40 percent. Larry Bernstein, Bell Communications Research

[Thompson's rule for first-time telescope makers] It is faster to make a four-inch mirror and then a six-inch mirror than to make a six-inch mirror. Bill McKeeman, Wang Institute


Currently I'm writing mostly C and C++ and I find it very useful to write prototypes, either in the target language or in Python, to experiment with ways to modularize the code and how the modules will communicate.

I only disagree with throwing the code away. On the contrary, sometimes I even use the last prototype as a sort of "executable documentation".

That strategy indeed saves me a bunch of time. However, at least in my experience, I didn't have success applying that to modern front-end dev: the complexity is frequently so high and the dead-ends are so numerous that I prefer to buckle-in for the rough ride and proceed to the code right away.


Oh FFS.

Writing code is like writing natural language.

It's editing that counts. Not the first draft.

Whether or not you should throw out your first draft is in proportion to its value at getting you to the final draft.

Not some axiom you thought of for a specific use-case and specific code base


The psychology of knowing ahead of time you'll delete all the code is interesting. Devs would be in hack-hack-hack mode and not worry about design - the goal is just explore all the territory to find the hilly parts and landmines. Move as quickly as possible and report back.

In general I think developers value their code too much and should be willing to scrap and rewrite, especially whenever requirements are changing. I chalk it up to everyone's desire to conserve mental energies due to not understanding what their energy peak/limits are.


After seeing multiple "first drafts" going into production over years, I've internalised that they shouldn't be even presented as ready to be shipped until and unless a draft passes multiple refractors. If something is unknown, it is better to explore possibilities and prototype in a prior sprint before picking the actual implementation. Take your time to figure out unknowns before writing the code you intend to ship.


Management doesn't care about your perfect code. You won't get rewarded either.


Go further.

One test is worth ten thousand opinions. What are a thousand tests worth?

Plan to build and deliver an expedient first version, then have a technical debt jubilee before building the second.

Build v1 in the most reasonable expedient (including not-scaled-up) way to deliver a reasonably full first feature set. See how it runs, what the users like/dislike/need/want, where are the rough edges both externally & internally, what the load cases look like, on various components, etc.

You'll find 80%-90% of your surprises with this.

Now, use your quickly-won knowledge of your actual system to plan and build the durable and scalable version.

Despite my intrinsic approach having always been about planning, generalizing, etc. (premature optimization?), I was fortunate enough to be in a situation in one startup where I decided to do this, and it worked out amazingly well. V1 got out fast, without worries about tech debt. Building the 2nd version based on actual knowledge and and the v1 tech debt jettisoned, allowed a 'real' version to get out faster and better.

There may be some problems with mgt politics, and it interfered in the above product also, but it does have much to recommend it.


I don't throw it away, I keep it as a reference. But for anything interesting, I might rewrite from "scratch" three times easily.


I've been following this flow where I write a few successive versions and perform refactoring steps to move one version "towards" another version, as if there is a conceptual force of attraction. It's often unclear which version will prevail, as if I'm acting out a genetic algorithm with a small population.


If you know that you're possibly keeping the code, you do things in a "proper" way, which means moving slower. Put in all the exception handlers, all the log statements. Structure the code nicely, refactor things while you're in there, modularize them properly. After all, it's going to be reused.

This is where it all comes from. You don’t have to throw away code if it doesn’t need refactoring, modularization and so on to satisfy that “reuse” mantra from the beginning. Healthy reuse comes in two flavors:

- a copy of the code

- a separate well-maintained, versioned and documented library

Anything reuse-focused in between is mostly snakeoil not worth drinking and is often the reason why your code has so many intricacies in it that you have to perform “recon missions” before actual coding or estimating. You’ve built a dozen of poorly-documented ad hoc libraries into your project. There’s no surprise it may require a proper source code expedition to detect and recrystallize parts that a new requirement shattered this time.


Eh, don't get me started on how many people take a well known abstraction then build another abstraction on top of it. You already had the abstraction!


I'm a big fan of Gall's Law:

"A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system." [0]

I don't throw my first version away, I just start really simple and evolve it iteratively, making sure it works at each stage, and by the time it's done very little of the original code remains.

0. https://en.wikipedia.org/wiki/John_Gall_(author)#Gall.27s_la...


Justify wasteful things you've done and suggest them to others.

Less cheekily -- it's true that sometimes the best way to research a topic is by writing code that you might appropriately throw away later. And it is extremely important to be able to recognize when something is not worth keeping. But also, often, the most efficient strategy is to think about the problem off-screen enough that you don't subsequently waste time on-screen writing things you won't keep.

It seems strange to usually expect to throw away your first draft. Not every problem is that hard, and even for hard problems, not every first attempt is wrong.


Often happens that you can't see the next hill until you get to the top of the current hill


Funny how Fred Brooks keeps repeating history.

I am just watching as the young and optimistic learn the hard truth.

Build one to throw away - mythical man month.


It feels as if a lot of this discussion happens between people who have a completely different context in mind where throwing all the prototyping code away may make more or less sense. For me, it is hard to think of a scenario of writing prototyping code for more a few days and not reusing at very least 10% of that code (with some modifications). I would be fine throwing away the 90% of it or sth, but the frustration having to rewrite parts that I had just written and threw away would be too much to bear. Even merely for reference, would make no sense for me to throw away.


  ...you will anyway (Brooks)
IMHO Write it once for the problem; again for the solution.

TFA stresses: finding out what you're building; finding the "unknown unknowns" (the things you don't even know you don't know, encountering those problems)

TFA doesn't say it this way, but I think the fun of throwaway-prototyping is your thoughts can focus uninterrupted on the problem, undistracted by secondary issues. You can hack-around tedious parts, now knowing they are there; and you can get absorbed in the genuinely tricky parts, unobscured (even if you don't succeed, you also know they are there). You're not expected to get it all right.

It's turning an unexperienced developer (on this problem) into an experienced one.

PS Somewhat disturbingly, Brooks went back on this according to this interview https://www.computerworld.com/article/2550685/the-grill--fre...

  "you should plan to throw one away. You will anyway."
  That was the first edition of The Mythical Man-Month. In the second edition, I say that was misguided! You ought to plan to continually iterate on it, not just build it, throw it away and start over. Some of the things I said in 1975 were wrong, and in the second edition, I correct them.


I have a similar process, but I do it after hours because I treat it as exercise.

The task isn't about what the resulting system does, but how - the aim is to figure out where certain patterns are applicable and where they break down.

Ideas include: using a design pattern like Entity Component System, exploring whether a certain way of formatting code is actually more readable, producing a drop-in replacement for a small utility.

I firmly believe this makes you an overall better programmer. That being said I think it would be wise to refrain from doing that at work.


This is the exact technique I used that made me a better developer. I would take on the task of rewriting code in my own time (small team where my rewrite didn't affect others). There were times where I misjudged how difficult the rewrite was, but I stuck with it and took it as a learning experience to make sure next time I thought things through further before moving ahead with a complete rewrite. Now that I'm more experienced, I tend to get more important (and difficult) projects, and no longer attempt the large scale rewrite. I do definitely throw away portions of code and rewrite, though. I think there's a balance to achieve.


I try to structure my code so that it can be easily tested and it is modular enough so that you can swap implementations with fair ease.

This means I can iterate much quicker - I can have code that works and is good enough for now and I can later on improve it and also I can suitable test coverage that can confirm the new improved implementation works as expected.

Code that is organised this way is also rather easy to understand and test perform a function of documentation - which means someone else can get familiar with it and work in a short period of time.


See also “Plan to Throw One Away” from the Mythical Man Month:

https://wiki.c2.com/?PlanToThrowOneAway#


I just lost a first version of some assembly code I wrote on my Apple II through my own mistake: my code wrote to the part of memory where the assembler kept the source code. When it seemed to work (I hadn't noticed I'd lost my code yet) I saved this (corrupted) version of the code to my floppy, corrupting even the floppy. I had to write the same thing from scratch again, but this time with the full understanding of the problem in my head, and I think the rewritten version is better because of that.


«In most projects, the first system built is barely usable....Hence plan to throw one away; you will, anyhow

Fred Brooks, The Mythical Man-Month

1975, fourty eight years ago, but worth repeating sometimes.


I worked one place that missed a golden opportunity to build one to throw away.

The short version is that the team formed and started working on the designated product, but very shortly after a VP-level exec got directly involved and started asking about where "his" product was. This was before I joined, so I'm not sure if the original direction was supposed to include this or if there was some management nonsense behind it, but the team had to quickly pivot and deliver something for this needy VP that was kind of sort of related to their stated goal.

In any case, the programmers got their marching orders and rushed out something to satisfy the VP, then went back to focusing on their original goals.

The real work overlapped, in technology and business function, with the rush goal. After they were done, they could have stepped back, taken a look at what they did to get it done, identify the shortcomings, missteps, and undesirable consequences of the decisions made for that work.

They could have proceeded with the main work using all the lessons from the 'emergency' work, and avoided a lot of the problems they'd created in rush to production. Instead, they decided to build, or perhaps more accurately defaulted to building, the real product on top of the work they done for the rush job.

After another six or eight months, the project was in serious enough trouble that programmers were leaving, and they ended up with an almost 100% turnover in engineers. The manager's response was to start micromanaging everything and set the team up for a death march to a predefined release date.


Not on this scale but it has happened to me on a few occasions that I've lost several hours work for some reason (including no autosaves).

After the initial despair I know from experience I need to quickly try and recreate what I've lost while it's fresh in my mind.

It takes much much less time and I always feel that I've done a cleaner job second time around.

At times like that has occurred that this is the way to do it but I've never been able to bring myself to adopt this as policy.


I’m in the middle of this with my new database startup. The alpha was written to gather requirements, so I intentionally skimped on code quality as it could be a waste of time. Now that I know the domain inside and out, the beta rewrite is worth turning up the quality. And it’s so fast and easy to write, as the old code serves as api documentation for all the low level stuff it depends on.

I recommend you do this too.


Anecdote: I know a guy who works solo and throws every project away (usually games) at least five times, often more like ten.

According to him, each iteration tends to go smoother and faster than the last.

Edit:

There’s probably some nuance to whether this approach is a good idea based on type of software, size of team, experience level, personal and/or team skill set, etc…


I've found this great on my personal projects where I've had all the time I've wanted. The next iteration was always orders of magnitude better than the first. In the professional setting I find that the prototype is often enough and rewriting costs unjustified amounts of money.


No, don't do it. Practice continuous refactoring.


This advice depends a lot on how your "first draft" was coded to begin with. The concept of "first draft" shouldn't exist as code. You design it properly, then you implement it properly.

Now, if for some reason, and there are plenty of good ones, you cannot do the former, then sure, you pay that price by redoing some work. But, at that point, you do the same thing you should have done: you design it properly, then you implement your design.

One could argue that implementing a first draft is a way of designing it. Which, I cannot imagine making sense unless you don't know what it is you want to end up with. Maybe if you're making computer games or other creative endeavors?


> You design it properly, then you implement it properly.

I’m extremely defensive and try to think through everything. Aside from analysis paralysis which is a real obstacle, it’s still not enough. Or rather, it’s not the best way to use a human brain. At some point for a complex project, no matter how smart and experienced you are, assumptions break down. The mental model changes based on “friction” with the gritty world. Prototyping is one way to confront wrong assumptions earlier rather than later.

All creative and intellectual endeavors have analogous process. Writers don’t structure a book and write it perfectly in a methodical fashion. Basically, turning chaos into order can – almost per definition – not be proceduralized.


> One could argue that implementing a first draft is a way of designing it. Which, I cannot imagine making sense unless you don't know what it is you want to end up with. Maybe if you're making computer games or other creative endeavors?

It's when the problem itself has some ambiguity. Like, I design systems that other people and systems use within a large company. I try to figure out how they'll use them, but even they can't tell me exactly. New business requirements come in mid-design, etc. They might even cancel the whole project if something more important comes along. In that situation, it's best to get some MVP as quickly as possible then iterate on it. Later iterations might totally scrap and rewrite the internals, which is fine if the API sticks.


That makes sense. Like a prototype you hand out so that you can figure out what it is you/client actually want. I don't think of those as first drafts though. Maybe it's a language barrier thing for me.


"Design" has a spectrum of level of detail, anything from a whiteboard full of information to a description of code in a fancy tool that is more detailed than the code itself.

Text (source code) as a representation of ideas can be worked on extremely efficiently at arbitrary detail. At low level of detail only beaten by a whiteboard.

If any piece of information has been missed when creating anything (any form of design or code), updating mutiple implementations of the same thing takes more effort than updating only one (the code).

Too many details in design will require frequent changes, with low level of detail it won't help much to detect missing pieces of the puzzle. Either way it adds effort.

Design has its advantages, lower total cost/effort in the short term isn't one though.


First draft could mean a lot of things. If you mean something you write then rewrite before it's ever released, the same still kinda applies. You might be mid implementation when something changes and ruins whatever structure you had. Or maybe the abstractions aren't very obvious at first even if nothing is changing.


> You design it properly

And how, if I may ask, does one design it properly?


That's easy. Create one, try to implement a bit of it, throw the design away, and then create the real one.


Yeah, well, good luck convincing most manager I know to plan for a phase whose outputs get tossed away. They would really much rather the prototype code gets merged, and then just work. Not to mention the fact they never let you "do things more properly", if they can help it, because you are again wasting precious time which could be used to do other work from the ever-growing backlog.

As for my own projects - if I both "write things more properly" and divide them into small parts, it's easier to just keep the code I wrote for doing whatever it is I had it do; and maybe at some point collect enough of that to make a library, or at least a reusable header.


You throw your first draft away, you go straight to the second-system effect:

https://en.wikipedia.org/wiki/Second-system_effect


What a load of nonsense.

Iterative code refactoring is the only right approach.

Why spend time writing code and then throw it away?? That makes no sense at all.

Best approach would be to spend some time thinking and conceptualizing BEFORE writing the first line of code.


For me, writing code is a way of thinking, and often the best way of thinking about a problem or question, or to start conceptualizing an entire project, is to write some running code as a sketch, a rough draft, a prototype.

If it has any merit, the running code informs the planning and decision-making process, stimulates thought and discussion. Parts of the draft, or even the whole thing, can be transformed, rewritten or refined, and become the basis of the solution and product.

Of course it depends on the goal and nature of the question. Sometimes it's better to talk and think things through in human language, deeply and thoroughly, before writing any code. It could be that the best solution is no code at all, to use an off-the-shelf product or service.

About "throwing away" code.. Imagine a musician with thousands of hours of playing, practice, informal or private performances and recordings - including piles of sketches and rough drafts that will never see the light of day. Only the best selection will be curated for the public, to make it into the final product. But the 99% that are "thrown away" was not a waste of time, they were a necessary part of the creative process to achieve the good stuff.


We do this occasionally as a tech spike - if there’s a lot of uncertainty or if we need to evaluate between a couple different approaches, we’ll dedicate 2-3 days to hacking away with the aim of lifting the “fog of war”.


I've always called this the "green-path prototype", which is building what you thought you knew, without implementing edge cases. The edge cases will get discovered and noted along the way and they should be documented (with a comment or other method that doesn't get lost). Pretty much any time you only handle a subset and don't handle the 'else's.

The real implementation should consider the green-path and edge case notes, decide the desired structure/decomposition and pick and choose from the green-path rather than try to start with it as v1 of a final form.


This is an idea I've preached for a while now. Software requirements, goals, deadlines- all that stuff can be planned. Even the architecture can be sketched out. But, for any sophisticated piece of software, you'll never get the actual code right the first time. If you think it's right, dig deeper. Find everything that's wrong with it, and do it again with those problems fixed. Great software and great art have one thing in common: the best version is never the first.


I've never had luck with this approach. For me, iteratively building and changing the codebase as new requirements come up lends itself to having to think of good ways to modularize and future proof. The idea of throwing out the code and starting over is definitely appealing, but often the iterative approach gives me something I'm super happy with.

I think I do use the throwaway approach with simple prototyping of smaller portions of code that I will then port into an existing system, but the overall system remains stable.


If you're discovering requirements along with developing code you often end up with Frankenstein's monster because things that were intended to do one thing got retrofitted to do 4 more things. It works but if you had all 4 things in mind when designing the first approach it would have been much cleaner. And going forward it keeps getting worse.


The article describes a potentially useful exercise that might be explored by an organization that has allotted development time for a hack-a-thon or some such. Depending on the size and scale of the projects that a software team typically develops, a more realistic approach might be to spend smaller and more manageable chunks of time as a team evaluating recently developed features/services/apps and directly applying improvements gleaned into the planning of subsequent projects.


Ship your first draft. Never rewrite. Go work on the next task.


First draft? Final draft.

http://m.quickmeme.com/meme/3629ez


The agile mafia does not appreciate your sarcasm.


> Organize hackdays! We do these at work, and they're a source of a lot of the ideas for and prototypes of major features that get into our product. When a feature comes out of one of these, it's already vetted and prototyped.

As someone who wins most of the hackdays, I agree that the original hackday code needs to be rewritten. I have seen too many spaghetti Frankensteins to allow quickly written code into a codebase.


Similarly why hackathons are so surprisingly successful in creating projects that would be otherwise doomed if estimated and planned out before hand.


If waterfall is:

  specify-design-code-test
Then agile TDD with prototype and discard/refactoring is:

  specify-test-code-design-recode
A POC is different from a prototype. A POC helps to define the problem space, but it may also suggest a better initial design (identify useful abstractions):

  poc-specify-test-design-code


I don’t really see the need to commit to throwing my first iteration out. I do refactor pretty aggressively at the outset of a major project or code change though. It often takes me quite a bit of time before things really get off the ground because of this. The result is probably the same—that I’m paying a lot of attention to making sure I’m going down a good path.


I take this to the extreme. Over the past 6 years I've thrown away...somewhere around 12-15 versions of my side project. Part of it is that my own requirements and opinions changed with each version, but another part of it is that I'm pathologically unhappy with my own work.

So yeah, maybe throw away the first draft, but don't do what I did.


I have a different approach: I'm ok to throw away significant chunks of code as long as I have important new ideas that allow me to do more with less.

Often the problem with refactors is that don't come with enough of these new ideas , there's no real progress, it's mostly moving things around.



I've seen the theory that this is why changing to a new framework/language seems like magic so much of the time. That it's not so much the differences in the technologies as it is your knowledge of the problem.

It's probably a little of both in reality but it's an interesting idea.


Not sure that level of absolute is the way to go.

If you build a prototype and happen to nail it then why not iterate?

Or perhaps you wrote a prototype and specific components/module feel sound?

Authors suggestion feels sound as a question you should ask yourself & seriously consider, more than a strict principle.


This maxim could apply to any skill based project. The skills needed to do something properly require first doing it from start to finish improperly. That's why a safe staging environment for skill development is essential to improving at anything.


I didn't read the article, but can guess at the rationale. I often wish I had the luxury of taking everything I learned writing something and then applying it when starting over.

But IAC... I would much rather throw away someone else' code. :D


Well, maybe not throw away everything, but I understand the principle. There are also other areas where this could help - and one of them is "naming". Sometimes when you start a project, you don't really know what the best names are for things. Project name, repo name, service name etc. So you choose something that you think it's ok and you go ahead. As the project progresses, you realise you've named things wrong, but renaming is so annoying and tedious. Often we ignore the bad names because it doesn't seem significant. But names are how people burden their cognitive load (an example, we named our repo after planets rather than describing what the repo does - "acme-customer-module-frontend" but we named it "pluto" - now everyone has to remember what pluto is and make sure pluto is not neptune).


I have never done that in my (successful) 30+ year career. I usually start with a simple end-to-end skeleton implementation that works, and then step-by-step refine it to production quality. It’s simple. It works.


Honestly for smaller projects I just keep throwing it away until I can write it again in a day and it's as simple as it can possibly be. Although I don't always remember to do this.


I like the idea of “Spike and Stabilise”.

Which means, prototyping in this way, but at the point it is actually delivering value, stabilise.

Of course, it’s never as simple as that, but there’s certainly a place for this technique.


I think the author mixed up prototyping with proof of concept. A POC should be tossed out. I think most companies expect at least the senior devs to be able to write a feature on first try.


I have the observation:- first version is never the best it will work with bugs, then you iterate and improve structure and arch. The third is the best. No time to waste doing that.


I'd have to be very new to something to throw away the first draft of my code. My goal is to have to change the code I write as little as possible.


IIRC, Michael Feathers calls this "Scratch Refactoring" in his "Working effectively with Legacy Code" book from 2005.

Excellent read even today.


I feel like this a symptom of trying to design while you code instead of putting down the text editor and just designing your solution instead.


I really like this idea, I'm often prototyping, but since I do it on my own, it is not throwable, so I tend to keep longer as they explain.


Or maybe keep it and tell GPT to refactor it. Soon.


Exactly what I thought after GPT3.5 was released. "Delete it all. Start from scratch. You'll learn so much. You'll do so much better. You'll actually live up to the scale of your creations' potential."

But a decision like that would be superhuman by itself, a transcendent experience, giving the creators a perspective that seems impossible. Unlike what is happening instead, both publicly and behind closed doors... A great pity!


Aka "Build one to throw away"


I also discuss this in my book Street Coder in the section titled “Write it from scratch”.


yeah but I often end up getting Second-system effect https://en.wikipedia.org/wiki/Second-system_effect


Someone said that to me once.. I just accidentally always end up with it in production.


I'm not going to delete my .emacs file even though it is ugly, it just works.


Can I just pass my first draft through chat GPT because I wanna save my fingers.


As a designer (UX focused, but technical as well), I have put a lot of effort to study prototyping in sw context.

I think prototype itself is fairly well understood as a noun, but how to approach the activity is not that well understood. I think it starts with what to prototype (scope/intent) and what to do with the outcome. This is where different viewpoints start to have their impact.

It is very common mindset not discard anything that has been done. Some cognitive biases are likely in effect here.

It is also common to attach additional intentions retroactively. E.g. it is totally ok to build UI prototype and to deviate drastically from the current state of things. Depends on the intention. Now, it is not hard to imagine a follow-up discussion that takes unfortunate turn at some point because the expectations for the activity does not match. The focus shifts from the main idea to defining what was not the idea. The value of the activity diminishes steadily. At worst, inexperienced prototypers might end up with a loundry list of changes to be done to meet the current state, confused about what just happened.

I think the main point of the article is valuable: be ready to stop and throwaway your prototype. Use it smartly. Find things you want to validate or just see, then be immediately ready to throw it away. Do not project additional things on to it. Use that energy towards the ”real thing”.

In the world of Figma/other prototypes there seems to float this idea that prototype has some fixed and standardized meaning. As an activity it has been bolted into different methodologies and tools, which is fine of course, but they are also taught, discussed and treates together with specs, requirements and other artifacts that serves different purpose. They communicate different things.

That’s it. In the end it is about communication. I think the great power of prototyping makes it also hard to use to it effectively. Personally, I think the best part of building a prototype is to throw it away. It is a milestone, the end. Almost always a success.


First do it - then do it right - then do it better.


Another untenable programming ethic used as a clickbait title.


I see. A new generation discovers Extreme Programming!


What this post says: "throw away the first draft of your code"

What every HN commenter is replying to: "throw away your code"

--

I've written software professionally for 14 years and just last week I wrote code for ~3 days and then decided I was on the wrong track, and checked out main and made a totally new branch, spent ~1.5 days re-writing my feature in a totally different way. I had come to realize the new way would be easier to write, MUCH easier to test, and a bit more maintainable long-term. Nothing wrong with throwing away un-merged code if you decide that merging isn't the right decision, and feel you have learned valuable lessons to inform the re-write.


A lot of writers do the exact same thing.


My boss doesn't allow me.


Only if you told them you were done, when you weren’t (if following an approach described in the article). I suspect the author also meant, it won’t take longer with this approach. Possibly even faster that without it is completely viable.


Or refactor it




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: