This is called prototyping, which is a valuable part of the design process; some people call it "pathfinding".
These are all inputs into the design. But a design is still needed, of the appropriate size, otherwise you're just making things up as you go. You need to define the problem your are solving and what that solution is. Sometimes that's a 1-page doc without a formal review, sometimes it's many more pages with weeks of reviews and iterations with feedback.
Don't forget: "weeks of coding can save hours of planning" ;)
I think that it’s one of those dichotomies. One needs both a design and a prototype.
As you say, weeks of coding can save hours of planning. But weeks of planning can be wasted, too. It’s easy to write things on paper which don’t make sense or are impossible, e.g. ‘We will colour the unicorn fleet semi-sad.’ Ideally, the design and the prototype would evolve in concert, each iteration of one driving the next iteration of the other, spiralling like the double-helix of DNA.
The great virtue of a bias towards building prototypes is that at the end of a round of prototyping one actually has software which can do something — at the end of a round of design one doesn’t really have anything.
> a design is still needed, of the appropriate size, otherwise you're just making things up as you go.
A design needs to be understood, certainly, but that doesn't necessarily mean a document, or indeed any permanent artifact. And if you do need a permanent record, a PR can be as good a medium as any.
> Don't forget: "weeks of coding can save hours of planning" ;)
I've found the opposite is true far more often. People plan and plan until the plan is not merely pointless but actively destructive to productivity.
Back in ye olde times a half hour or so with a chalk board and some smart people, go away try some things then come back and erase / write some more worked pretty well.
This is more closer to throwaway code than design docs.
Why not both? And it can be done productively: start coding (prototyping) if planning is difficult and vice versa.
That rests on the assumption that you'll notice that planning is "difficult". Whereas what I've usually found is that people build elaborate plans and feel like they've made progress doing so, only to discover when they come to implement them that there was a false assumption in step 1 that renders everything else pointless (or, worse, the plan should really be fundamentally reconsidered in the light of this info, but you've spent all this time on it, and it's not outright impossible to keep following it, so...).
An extension of this is what I call problem solving by differentiation. People who don't understand the problem space defer the part they don't understand by creating premature abstraction upon premature abstraction. In their head they seem to be slicing up the problem until there is none left, but in practice this continues until they finally understand the core domain problem or someone who does gets assigned. But those layers of abstraction don't die...
I thought this is a solved problem. Steps:
1. Have some long term goals.
2. Design a small iterative improvement or a minimum viable product that should bring you closer to your goals.
3. Build just that.
4. Reevaluate.
I.e. I thought the idea is to embrace changing the approach or even pivoting when necessary.
Came here to say this.
Right on the money:
Prototyping and pathfinding are totally fine and mostly necessary.
*But*, Software Engineering without design docs or any kind of specification (even if succinct) is just tree-house building, *not* Engineering.
And the bigger the size and importance of the project, the sooner the problems and technical debt will start to show.
Very interesting! Is "tree-house building" a reference to something specific or did you make it up? It strikes me that engineering is either a spectrum, or it's a segment of a larger spectrum (the name of which I don't know). Tree-housing is near one end of that spectrum, the MacGyver end, and dam-building is near the other end (where the problem is thoroughly researched, the goals are calculated, and the designs are all made to serve those calculations specifically).
Do you feel "engineering" could cover the entire spectrum, where your "Engineering" specifies everything on one side of "has a design doc"? If not, is there a name for the whole spectrum? Does crocheting from a pattern sit anywhere on this spectrum, or is it something else, or does it mean that my spectrum-of-engineering is an illusion? Open questions here, not directed at DrNosferatu necessarily.
Agreed, why not both? In fact, I’d say, first write up a theory, then demonstrate that it does or does not work using a prototype, then write the actual design doc. At all times, and continuing into the implementation phase, prioritize the disposability of your code. The easier it is to delete, the better.
I think my argument would be that they could be the same artifact. Hence my usage of Draft PRs with a lot of rich documentation. And a willingness to throw it all away if you don't think its ultimately the right approach.
The real problem is the industry's inability to throwaway first solutions, so we introduce this (IMO inefficient) 'design doc' step as a safety mechanism
I enjoyed the writeup and think it's a nice idea depending on the situation, but with serious limitations (for context, I also do prototype first, but still right a design doc at some point)
The main issue to me is exactly that we do throw away the prototype implementation.
Writing rich documentation for throwaway code is tedious, and at the early stages I see a split between the people that mostly read the doc and only glance over the code, those who almost ignore the docs and focus on the code, and some rare people actually looking at both and getting confused at the discrepancies ("you say you in your doc you store values in the remote database, but this code only writes to the local one, which one will stay ?")
Perhaps I'm just not good at docs, but doing code heavy prototypes with only very light and high level doc was way more efficient.
Then when we settle on one direction, I can freeze the main points in a separate documentation and rewrite the prototype in a more serious version.
Having a separate design docs also helps when the system components are split across many area. Having the doc even when it's a compact proposition standardizes the process and you never have to ask upfront if it will be needed or not.
I have to say that I completely disagree with you on this, but I also understand that I am a lot more comfortable writing prose than the average programmer.
A draft PR with rich documentation doesn't often communicate the "whys" and the "why nots" nearly as well as a well-written design document, or the high-level goals of a piece of code. It also is much harder to share with the product and business sides of a company which may be less comfortable with the idea of reading code.
A great design doc can reduce non-obvious problems to trivial and is usually written alongside a prototype (if the thing is simple enough).
I will give you that if you are instead competing with a poorly-written design document, a very well-documented PR may be a good alternative.
Writing is really beneficial for exploring the problem space.
Many times I’ve confidently thought I understood a problem, started writing about it, and come away with new critical questions. These things are typically more visible from the abstract, or might not become apparent in the first few release milestones of work.
I’m reminded of a mentor in my career, who had designed an active/active setup retroactively for a payment gateway. He pulls up a Lucidchart and says “this diagram represents 6 months of my life”.
They’re not always necessary or helpful. But when they are you can save weeks of coding with a few days of planning.
I had a boss who had a math degree. He'd map out the flow from start to finish on a whiteboard like you see mathematicians on TV/movies. Always had the smoothest projects because he could foresee problems way in advance. If there was a problem or uncertainty identified, we'd just model that part. Then go back to whiteboard and continue.
An analogy is planning a road trip with a map. The way design docs most are built now, it shows the path and you start driving. Whereas my bosses whiteboard maps "over-planned" where you'd stop for fuel, attraction hours, docs required to cross border, budget $ for everything, emergency kit, Plan A, Plan B.
Super tedious, but way better than using throwaway code. Not over-planning feels lazy to me now
Sure, everyone has a plan until you get punched in the mouth; however, that saying applies to war, politics, negotiations, but not coding.
Yes I have to second that. MLJ.jl is also written by a mathematician and the API is excellent. Truly well thought-out.
(If you think “why does MLJ.jl have so few stars?” please keep in mind that this library was written for the Julia language and not for Python. I honestly don’t think the library is the cause of low popularity. Just wrong place wrong time.)
> Super tedious, but way better than using throwaway code. Not over-planning feels lazy to me now
Certain projects have too many unknowns to overplan and you need to collect data to construct the assumptions necessary to evaluate the approach.
First you have to have smart people who will be able to foresee design issues.
That’s a bit uncharitable but following this line of thought - you also need those smart people to be confident and communicative.
And for them to be listened to, what is independent on how well they communicate; and for them to be aligned with the most powerful stakeholder, what is almost never the case; and for no big change to happen in an uncontrolled way, what powerful people nowadays seem intent on causing all the time.
If you create the plan like a mathematical formula like my boss did, the evidence becomes irrefutable... like a mathematical proof. The article does mention that the plan is communication tool.
> however, that saying applies to war, politics, negotiations
It’s not even an argument against planning. You’d be a fool to go to war without a plan. The point of the saying is that you’d be a fool not to tear up your plan and start improvising as soon as it stops working.
It is kind of an argument against overplanning though, because if your plan that you spent considerable time creating becomes irrelevant, you wasted a lot of time
That assumes the plan itself is the only useful output from the time spent planning. Even if the plan itself isn't used, the time spent planning means you examined the problem thoroughly, and raised questions that needed answering. Taking the time to think about those questions in order to give a coherent answer is, in and of itself, worthwhile for answering the question later, even if that part's never actually written down.
True, I agree 100%, and that's why I chose to say 'irrelevant' to imply that there was nothing useful about it inherently for those cases. Most of the time, at least in coding, there was probably something useful that came out of it, even if you had to scrap the plan. At the very least, some sort of learning more about the problem space. In the case of war, however, if you lost the war because you over-planned (such as planning one thing very very intricately instead of having several rough plans that leave room for some improv), I'd argue that there probably aren't any residual benefits to celebrate
To borrow another quote:
Plans are nothing, but planning is everything.
The process of building a plan builds the institutional knowledge you need to iterate when inevitably the original plan doesn’t work.
I had to do this for a patent application, and likewise found it very useful for identifying holes in my thought process or simply forcing myself to do the functional design work up-front and completely.
It was also great for brainstorming about every feature and functional aspect you can imagine for your product, and making an effort to accommodate it in your design even if it's not MVP material.
> but not coding.
In my experience it applies to coding when you have any reliance on third party libraries or services and don't have an extensive amount of actual real world experience with that technology already.
If you have unknowns, then your planning process starts with, "let's figure out how to use this new technology." And that process can involve a bunch of prototyping.
Having to make a choice between "make a design document" or "do prototyping" is a false dichotomy. They're complimentary approaches.
This right here <- is why every discussion in the SWE space is super tedious. Every critique of anything is really just "you are holding it wrong".
Over-planning is impossible if you plan for it, thanks!
> Super tedious, but way better than using throwaway code. Not over-planning feels lazy to me now
How was it better? I think a lot of people plan precisely because it feels virtuous, but that's true regardless of whether it's effective or not.
My boss would take a piece of data/input and run it through the entire process. It's a string data here, converts to number here, function transforms it here, summarized here, output format there... You wouldn't run into data type issues or have an epiphany that you're missing a data requirement.
>Sure, everyone has a plan until you get punched in the mouth; however, that saying applies to war, politics, negotiations, but not coding.
hey, the EU just introduced this new regulation is the software version of getting punched in the mouth.
I the book How Big Things Get Done they analyze big and small project failures and success and end up with something along the lines:
1. Spend as much time in planning as necessary, in the context of mega projects planning is essentially free, maximize the time and value gained in planning.
2. Once you start execution of the plan, move as fast as possible to reduce likelihood of unforeseen events and also reduce costs increases due to inflation, interest paid on capital etc.
[0] https://www.goodreads.com/book/show/61327449-how-big-things-...
+1 for "How Big Things Get Done". It changed the way I run projects. I got lucky in the sense that I was able to convince my corporate overloads to allow us to have separate Discovery and Delivery goals, on the premise that discovery is cheap and delivery is expensive (the former significantly reduces risk of the latter) and we show our work. Discovery goals come with prototype deliverables that we're ok not shipping to production but most times lay the foundational work to ship the final product. Every single time we've found something that challenged our initial assumptions and we now catch these issues early instead of in the delivery phase.
We've fully embraced the "Try, Learn, Repeat" philosophy.
> convince my corporate overloads to allow us to have separate Discovery and Delivery goals
Since I’m in the middle of trying to do something similar, would love to hear more details. What kind of goals, whats the conflict?
Man plans and executives laugh
I agree writing is beneficial. But I also find this works with coding. And they go hand in hand for exploring in my experience.
And in the end a good PR has a lot of writing too and has this effect. IMO this sort of well documented draft PR serves as a better design proposal because pure writing causes you to forget important constraints you only remember when you’re in the code.
Agreed. You can write prose or implementations or tests beforehand and I don't think it matters too much which you choose, just as long as you give yourself a phase 2 for incorporating the learning you did in phase 1 and put a reality check of some kind in between them.
The only problem with having the draft being implementation is that maybe you'll get pressured into shipping the draft.
I’ve had to ship the draft a few times in my career. Usually when the actual code would have been weeks or months more of work (draft has poor architecture, while a proper architecture would have been just as much work as the draft). Twice it was due to showing a demo and a decision maker in the audience said “we can sell this tomorrow” or something to the same effect for that org. In one case, we ran a simple a/b test as a proof of concept on whether to pursue the idea further and it added an extra million bucks a year in revenue. Nobody wanted to wait for a proper implementation. All that code is still in production, slow as shit and nobody wants to fix something that isn’t broken.
If you have a draft, keep it to yourself. Use it as a personal reference when writing the design, or share snippets. Other engineers will realize you have a draft, business people won’t.
> In one case, we ran a simple a/b test as a proof of concept on whether to pursue the idea further and it added an extra million bucks a year in revenue.
I'm with the people who decided to ship this. The organization will need to fund more maintenance than they would if they waited, but that has real costs. And "keep your 1mm/revenue idea to yourself" doesn't sound like a healthy engineering culture either.
Heh, yeah, that is a special case though. Most people don't work on things that has that kind of impact. If it might, I would suggest going against my own advice.
The time spans for doing it properly or shipping are maybe a bit muddled here, but if you can make a million dollars by shipping something now, you should probably ship it now. If the code is that bad (ie can end up costing way more to maintain than fix) then you should afterward immediately fix it. Side benefit is you’ll probably learn a lot about doing it right from the prod system.
Oh we tried to fix it and never succeeded. As soon as other teams found out about it, they started using it too. To be honest, had we waited to do it right, we potentially could have made even more money.
I'm not sure what you mean by "should". If your job is to build things that aren't going to fail in ways that later end the company or hurt people, then finding ways to keep the:
> But we could make an additional million next quarter if we ship it now
...crowd in check is probably what you should be doing.
I like where your head is at but if you keep it to yourself then you can't get feedback on it.
I once did the draft with ncurses as a hedge against it becoming the real thing. It didn't go over especially well but it was fun.
"Writing is nature's way of letting you know how sloppy your thinking is." -- Dick Guindon
The biggest issue that I’ve had with design docs, is that nobody reads them; even if required by their employer.
The biggest issue that I’ve had with prototyping, is that people consider it “ship” code, and force me to use it as final code.
I find that I’m best served with a hybrid approach, where I spend a lot of time planning and documenting, but basically, for myself, then writing ship-Quality prototype code, so that using it in the end product is OK.
The reason people don't like to read the average design doc is because the average software engineer doesn't have enough writing skills to express the concept clearly and concisely without editing. The design doc becomes a mumble jumble of raw notes that nobody really understands except the author. And then people dread reading this kind of raw notes.
However, if someone tells that design doc writer that this is something like a term paper at school that is being graded, they can actually improve their writing by quite a bit by doing some editing. So the symptom is really the same as prototyping: people write draft-quality design docs and magically hope it to be a good quality piece of writing suitable for a wider audience. What they need is a few rounds of editing, just like when they write prototype code they need a few rounds of refactoring.
i originally wanted to comment on this to disagree with you. then i sat and read your comment in full and realised i agree with you.
with a weird caveat …
if you don’t have an audience who is willing to read what you’ve edited, editing can be a partially pointless excercise.
last shop i was in i’d edit this stuff to glorious beauty. no-one else would ever read it. i tried presentations instead. didn’t help. i tried live sharing the doc in a meeting, no one cared.
but editing a design doc (or a big PR description or whatever) is always helpful for me as it is a process where i’m editing the problem/solution description without having to move code around.
i’ve done a few good refactors off the back of editing some text/diagrams.
lesson i learned: edit design stuff as if someone else will read it, but do the editing for myself (to learn something or clarify something about the problem/solution).
As much as I get frustrated with GPT4's limitations when it comes to code, it's quite good at expanding detailed bullet points into solid design prose.
You still have to read it back and triple check it, as you would your own work, but you don't need all the time it takes to extensively expand it from draft.
Also, which it is in context it's quite good at documenting what needs to be done to test it.
I apologize, but GPT4 is absolutely not good at doing that. It is good at adding filler words and a self-important writing style, but if what you have can be expressed in a bullet list, it will not be better expressed by adding fluff and puffery. I can see it being a big aid if you don't know English very well and need it to correct your grammar and word choice, but not much beyond that.
> It is good at adding filler words and a self-important writing style
That's what they said—design prose!
I agree. I have used it for just that.
Why not get it read by a couple peers and iterate on their feedback? My writing is not great, but with the design docs I created, the first version is always terrible, not easily understandable mostly because of not listing all the assumptions whereas the third version almost always becomes something I would never be able to achieve myself. Over the years, I settled on 2 rounds of review. First to mostly add missing sections and rearchitect the design and the doc, second to mostly hone the explanation/details.
You have nice coworkers willing to read drafts. If your coworkers are busier (if they are more senior) they might not. Or they might read half a page and then stop because there isn't enough context and background information in the doc. Or they might read something and misunderstand it because of bad writing. I'm usually not the kind to waste other people's time like that.
Or, they won't say anything about it at all, because they (choose all that apply):
a) are not good communicators,
b) do not respect you,
c) are afraid to admit they don't understand (even if your doc is clearly shit),
d) do not care very much about the project.
In my experience, it’s usually (d), even if they are on the team.
Add to that engineers that don’t like/aren’t good at reading (at least in my time Zone) and you end up with people asking for stuff that is documented or even understanding the complete opposite of what was written.
its not just your time zone ;) and its not just engineers.
We had to build and launch something by a deadline in order to avoid renewing a contract that would have cost $x mil. But we discovered we weren't going to finish in time with the resources and the approach that was planned.
So I got approval to hack out a temporary impartial suboptimal version and we were able to take off in time.
This has allowed us to fly for a bit while others finish building out the permanent proper version of that part of the wing.
In fact, during our flight we discovered missed requirements in the original design. This has delayed that proper version's release to prod. But I've been able to add that quickly to my hack and keep us flying.
My hack doubles as a production support tool. And as an alternate route if the permanent version needs to be paused because of a bug. Yes it's a partial imperfect hack but it has benefits.
Someone has complained about the language I used since it is less common. But remember, we weren't going to be able to take flight with the existing resources and approach.
We would have needed more and/or faster developers in the favored language to meet the deadline.
If any present employee (including me) had bandwidth and was able to be as productive in the favored language as I was building my hack in the uncommon language, that employee would have been tasked to build the permanent solution on time. That option was not available.
Anyways, if you have an existing production support tool, it's also a place where prototype features can live for a while.
What was the language?
I think it's not uncommon for a dev to be faster with one language than another. And for another dev to know the same two languages but be more productive in the opposite direction. (And I am mindful that the bar was much lower for me -- being a temporary hack, my stuff didn't have to integrate fully or have the same level of production manners)
But here are two factors that make me faster in the uncommon language.
You know the usual debugger experience where if you keep stepping or you set a breakpoint and then later realize the interesting part is actually behind you? Which means you have to start your scenario again?
Well, the uncommon language has a recording debugger so you can visit any points in front of or behind you, and see what any variable or return value was. You're not limited by the current stack because the recorder captured every package you told it to from the beginning
Even a third party package...
This means I only have to debug once to find the bug, not n times.
And the language is expressive and pliable enough that this debugger was implemented in the language itself...
And mostly by one guy -- not me of course -- it didn't take a whole company to implement it.
And you know how in some languages like Java if your car (program) starts acting up in LA, you have to update the blueprint, send it to the car factory, build the new car, drive it back to LA, and try the same left turn to see it it still ferks?
With the uncommon language, I can stay in LA and change the car while the engine is still running, n times before the 2nd Java car arrives. The language is Clojure.
It was clear that the language was Clojure by the middle of the third paragraph.
Rust and Clojure are the only languages that always come with unsolicited sales pitches. Not knocking the languages, just observing.
I was half expecting this comment to end without actually naming the language.
I have a friend who told me two separate times without promoting about how great a particular brand of guitar strings are... Both times I reminded him I don't know how to play any instrument.
Sorry. I almost resisted naming the language. But what got to me was my co-worker's complaint was about the very tech that made it possible to launch + save $x mil + save some nights and weekends and holidays for a handful of other devs.
And that the complaint was: the language choice makes it less maintainable (hiring pool is smaller) -- but the two factors I mentioned above actually contribute greatly to maintainability.
My original comment could have just been: "a production support tool can be a place to prototype new features."
And _other_ teams could have used other tech to solve the problem. But for the team that had the context and the responsibility, this was the highest probability shot. After hitting the target, a complaint was not the expectation.
Down votes are interesting when applied to a comment that's an answer to a direct question. (this isn't directed at sd9)
> Down votes are interesting when applied to a comment that's an answer to a direct question.
I suspect it's because your answer was indirect and had a fairly proselytizing tone. I found the information interesting, but it was a somewhat irritating read because it was inverted to demand attention, like a story, rather than being upfront. A better-received comment might have read more like:
"I used Clojure. This was mainly because it's my favorite language, so I could work faster in it, and because it has certain features which make debugging extremely fast. et cetera."
For what it's worth, I enjoyed your tale, and I like Clojure
The downvotes I'm assuming are because you replied 303 words to a simple question, with the actual answer at the very end which is frustrating to readers. Not saying what you mentioned wasn't interesting though.
Another opinion piece, but no data, not even a concrete example.
I get it, every software engineer has strong opinions, but that’s a weak one. If you think writing a lot of code to see what sticks is the job description, you’ll be replaced by GPT in no time - it can do it faster and cheaper. The challenge is always in getting alignment on what should be built, you won’t code your way out of that.
Hard agree. I don't know if "design doc" is the right word—I call them technical analyses—but writing up a doc that connects business & product needs to implementation details is highly useful to get everyone on the same page in terms of requirements & deliverables.
If I'm clear on the requirements & everyone else is clear on what I'm delivering, this is unnecessary. Sure, jump straight to prototyping. But this is rarely true for any serious project. There are always unknown unknowns that you need to tease out from the stakeholders, and a technical analysis is a great way to do that.
> If I'm clear on the requirements & everyone else is clear on what I'm delivering.
That's a big 'if', and usually isn't possible without prototyping in my experience. What you're describing seems like something that would be written after a prototype is already done. Presenting a prototype (or iterating on multiple prototypes) is a better way to tease out unknowns than any document.
It's usually not possible without a technical analysis, is my point. A throw-away prototype takes much longer to build and is much harder to review and discuss with stakeholders than a document. Like, how are you going to build a prototype demonstrating design trade-offs—are you just going to build every possible version? Are you really going to make a product manager read a 2000-line PR instead of three pages of bullet points?
Presenting a prototype only really make sense for UI-centric things anyway, and even then, there's a million ways to make a UI mock-up that don't involve functioning code.
It's also easier for all involved parties to read, whether they're technical or not, assuming you have some sections that explain things without relying on code.
Yeah 100%. I'm not going to make my product manager read a thousand lines of code just to get them to confirm all the assumptions I made about what they were asking for when I could just clarify.
That’s exactly my point, I think you get better alignment with “show don’t tell”.
Rectangles and dotted lines only get you so far. Being removed from actual code makes you forget the real constraints - the stuff that actually slows you down doesnt appear in google docs. “Here’s what I’m thinking (points at Draft PR)” gets you farther in my experiencre.
And yes it’s 100% an opinion. It’s a personal blog not a peer reviewed article :) I’m happy to be wrong.
Design docs are much easier to be reviewed. Throwaway code/a prototype is a huge amount of content where it's easy to miss things.
When I go back to understand a component, I want to know why the decisions were made. A well written doc illustrates exactly that - here are the options considered, here is why we chose this option to be implemented. It can be quickly read and searched.
Throwaway code - even a pull request with comments - is not the same. It takes much longer to process, much longer to review, and it's easy to miss seemingly obvious things because the important bits are surrounded by a bunch of unimportant boilerplate.
Put another way: anything that could be represented by a single PR is generally not significant enough to need a design document. Anything significant enough to need a design document should be a chain of smaller PRs / commits which allow the pieces to be reviewed thoroughly by themselves and have tests to show that they each work.
I guess this varies largely with project, stakeholder, etc. The kinds of projects I have been involved, showing a PR would’ve been either useless to communicate anything, or have to explain why it’s “throw away” and why we can’t ship the prototype.
That’s why I stressed it’s an opinion piece but no data. Unfortunately in this industry we have a lot of experience reports, but lack actual data on what is or isn’t more effective way to work.
Throwaway code is better than a design doc precisely because it is a concrete example.
Without something tangible like code to tether conversations, discussions over abstract designs invariably devolve into "my imaginary piece of string is longer than your imaginary piece of string" arguments that lead nowhere.
It's more likely that someone who holds this belief will be able to move substantially faster with an LLM, than it is that an LLM would take over this task in its entirety.
Sometimes the "data" behind this kind of article can be decades of personal experience.
I think there's merit to both design docs and prototypes.
At the same time, your argument that "you'll be replaced by GPT in no time" is also an opinion that you've not supported with any data; the same thing that you're accusing the OP of.
I mean If I stopped reading opinions, 99% of the HN comments would disappear.
In my experience there is a MASSIVE difference in the type of feedback someone gives on code vs a design. A design doc encourages "why" questions to get everyone thinking about the problem space. For example, a design doc comment might be "why are we suggesting a Rust Webserver when nobody in the company is proficient in Rust yet?".
In my experience those more subtle questions are much harder to raise once a prototype is working - "why does the team's experience matter? look how great it works! If you don't block it we could get this to production in a week by just polishing the prototype!"
This is not neccesarily a bad thing though. Lots of "why" questions are really counterproductive bikeshedding. Especially when it comes from just reviewing a design as opposed to working code.
Design docs are great for high-level orientation. If you are laying out all your code structure around it, you are doing it wrong. It's great to use the for what patterns will be used (using a facade pattern for example). It gives eng's a guide to how to implement at a high level and it should identify points of non-functional requirements; speed requirements, monitoring, security, etc. Throwaway code doesn't even touch on that. It should also put in scope some of the architecturally significant decisions that need to be made. While throwaway/prototype code can address some of that, it doesn't always identify it and talk about _why_ it is important.
In short, doing both is the best thing you can do and you should scope your design docs correctly.
We imagine software efforts that go through a clean, neat flow:
We write a design doc. Then make small incremental changes in a PR to rollout the functionality. Our git histories look clean and orderly. Like a steady march of progress.
Who imagines this? Professors who teach software engineering classes?
This reminds me of people who think that you write prose (essays, stories, novels, etc) by writing an outline and then "filling it out" with prose, as if in the process you will never discover anything that requires you to rewrite or restructure the document. Nobody writes that way. First drafts are always terrible and basically all good writing has been heavily revised.
Writing code is much more like writing than it is like building a house or a bridge.
For myself, I always find great value in debugging new code that I have written. Something about stepping line by line through new logic, and viewing variables/memory really helps me to improve my code. "Oh, I don't need that local var." "Oh, I should add a temp var to make the code easier to debug here." "Oh, this code is weird when the collection that we iterate is empty." No matter how old I get, and no matter how much code I write, I always discover new things when I debug my newly written code. I guess this could be compared to an author writing a first draft, then going back to read it... or reading it aloud to themself or another person.
When a stakeholder comes back 6 months after project launch complaining about missing/incorrect features, I can't use throwaway code to prove the project was built to spec.
I think it all depends on the size of the team.
Very small team and too many design docs, not good.
Huge team, multiple timezones, multiple squads, and few design docs, not good either.
And then you balance with all the values in between depending on your team size & culture.
Even within the same company, your approach will/should change as it grows. There's a critical point where move fast and break things approach will eventually end up with too many outages, production bugs, unpolished/confusing product, and last but not least, FTC eye watering fines.
I really like this process: using an ongoing comment thread to document design decisions along the way, as opposed to trying to formalize them in a single document.
I use GitHub issues for this myself but that's functionally equivalent to using a PR - a PR is effectively a GitHub issue with an attached branch of code.
I wrote more about my process here: https://simonwillison.net/2022/Jan/12/how-i-build-a-feature/...
But then how do you communicate the latest consensus on all issues, e.g. for a newcomer who doesn't want to slog through months of communication, or a team member who has been part of the thread all along but can't easily find where the team agreed on a particular thing? In other words, how do you summarize the thread into final documentation?
The documentation that needs to be kept 100% up-to-date is the usage documentation. Here's how to use this Python class library. Here's how to use this REST API. Here's how to use this CLI tool.
THAT should live in the same repository as the code itself, and updates to it should be enforced as part of code review.
Design documentation that describes the process that got to that point should live separately from the repo in something like GitHub Issues, because it's not guaranteed to be up-to-date but can still be referenced during code archaeology sessions when trying to figure out WHY the software is the way it is.
Commit messages work too, but I prefer to have a short commit that links to an issue thread where I can find out more.
I wrote about this here: https://simonwillison.net/2022/Oct/29/the-perfect-commit/#do...
Agreed about code usage documentation, although it's nice to have a more robust manual with several nicely formatted usage examples, which would be too much to put in the code as comments.
In my mind, the design document doesn't have to describe everything that got to the present point. It should describe the current working of the system, which parts talk to which, what protocol they speak, the details of any custom protocols, what the DB schema is, system architecture, why a certain index exists and why other indexes were explicitly omitted, etc. It doesn't have to contain every one of these things (perhaps the schema definition goes in the code repo) but these are the types of things I think it should have. It can certainly contain notes like "We used to do X but because of Y we now do Z instead" but that's not as necessary.
I would argue that such a design document is essential for keeping everybody on the same page. How else will you know the overall design? If you can fit all that documentation into comments in the code repo, great, but then you don't have a single point of reference to guide you; instead you have to hunt all over for comments that are stored disparately, and there is no overall narrative.
Well unfortunately there’s never “final” design documentation. Design docs go out of date pretty fast.
I think it’s healthy to treat design docs like a historical artifact - true at one point in time. It’s less likely they stay up to date to reflect reality.
I don't think they are mutually exclusive?
Design doc is broader. It's goal is communication.
Sometimes you need to communicate other than in code. Diagrams, images, words etc...
Agree.
It’s very hard for anyone who isn’t the author, or otherwise intimately familiar with the code, to understand changes at a glance. The reader needs high level explanations and documentation to quickly build the correct mental model to understand the change in context.
If you can look at 1000 lines of diff and accurately tell what it’s doing, and much more importantly, the upstream and downstream implications… you’re either lying, or somehow work in a perfect hermetic vacuum of verifiability that I am very jealous of.
A design doc would help narrow down the number of prototypes to 2 to 3 in all the available options. This is especially useful if you are exploring adding something completely new.
Though i feel that showing is better than telling, when new people onboard they have a easier time understanding via a design doc than code.
Does your task need great code?
Not a rhetorical question. Often the goal is the functional minimum. If that's your gig, it's hard to appreciate code quality maximization, and vice versa.
If you do maximize quality, I'll one-up the suggestion in the article. Treat your first write as throwaway code. And your second. Up until the point where the rewrite would be roughly identical.
It's basically early refactoring, but with the code at its freshest in your mind. Coding it the first couple times implicitly maps out the problem domain.
You also leave no internal technical debt on the table. A surprising amount reveals itself right after you wrote it in. It gnaws at your ability to proceed. Subconsciously, it splits your attention in two: how the code is and how it should be. With each "fail" your attention spreads out and thins out.
Finally, this habit makes you more fluid in the language. Quality-maximized code takes longer, but your actual typing rate ends up being much faster.
The design document is for documenting what problems you're trying to solve, and (critically) what problems you're not trying to solve. And the relative advantages and disadvantages of competing approaches.
The mistake people make with design documents is going into too much detail on -how- you're going to solve the problem. That's where prototypes ("throwaway code") come in. The design document should just be a high-level overview.
This makes me shudder because I've never seen this done in practice. What always happens is the code builds up like Jenga until no one wants to touch it.
And it's so obviously bad that new hires want to re-implement from scratch even though they don't have complete knowledge of what the software does.
Design documentation should be done on a whiteboard. You have limited space but that forces you to write down what is important and who it doing what. in a few weens it is so different from reality that you don't feel bad about erasing it for whatever is next.
... as long as you then take a photo of that whiteboard, attach a timestamp and put it somewhere people can find it in the future.
Otherwise it's not documentation at all, it's lost to the wind.
Chatis the point - it should be lost to the wind. I have never seen design documentation worth reading except in textbooks where it is only used to explain pattens to students.
Right, and my point is that this stuff genuinely matters and is worth recording - but needs to be recorded with a very clear timestamp (hence my preference for comments in a GitHub issue or PR).
Otherwise you'll inevitably find yourself re-litigating old decisions because you never recorded WHY you chose one approach over another.
Why is important but that is a very different thing from the desing documents I used to have to write, those would not tell you why.
I'm a big fan of prototyping to explore a problem space. I think some initial design sketches are useful, but you often reach a point where you can only learn by implementing. I've encountered so many design constraints in the past that I couldn't have planned for, but perhaps others are better at comprehensively thinking through a problem space than I can.
I agree with the blog post that doing a hacky solution is very helpful to understand the problem space better, although I think it's something that's challenging in an organization. In a lot of orgs, it might be viewed as a waste of time to work on something hacky. It could also come across as wasting people's time by engaging in too many discussions over PRs. People want shipping solutions today, not explorations that might need to be rewritten later. Asking for feedback might also come across as someone being unsure of a solution and needing "help" on it.
The dynamics of working in a team generally nudge devs to make more conservative moves and writing more up-front design docs, which will be slower and safer. I'm not arguing that's a bad thing, though. You need the documentation to communicate intent across an org and many other people will need to pick up the work, too. A PR may not provide enough space to comprehensively explain the overall design.
As an indie dev now, I find sketching some designs, followed by prototyping, then "massaging" the prototype into a working, shippable solution the ideal workflow. However, it only works because I'm solo and don't need to worry about getting an okay on potentially risky solutions or having to communicate and get buy-in on implementations. In a company that has lots of other devs and lots of paying customers, I wouldn't be able to do things that way, and for good reason.
> In a company that has lots of other devs and lots of paying customers, I wouldn't be able to do things that way, and for good reason.
Why not? You do the "sketching/mockup" of most of the flow (without going into too much design detail) in order for the team to understand better what you are going to build. Then, everybody can provide feedback based on their perspective, do a couple of iterations, then prototype (which if done well will be the initial version of the shippable solution) and then iterate again.
I've been doing this for more than 30 years. Never seen one design doc. At least not one that was actually used to guide the subsequent implementation.
They both have their place. Ideally the design doc flows out from the proof of concept to document the non-trivial assumptions and decisions. But nothing beats an actual PoC to demonstrate that something will actually work.
I like the phrase “weaponize” to refer to the evolution of a PoC into a workable application.
A few more years, and same here.
Design discussions can be very useful though.
I like the idea and we use it at work often. One pitfall one has to be aware is that it is easy for the people who are asked to provide feedback on the prototype to get bogged down in the details.
What you want is people to squint and tell you if they like the general approach, but what often happens is they tell you that you need more unit tests and you could use a ternary operator here and here. (The particular details will of course differ in each case.) The reason for that, in my opinion, is that your thing looks like a PR and therefore people default to the kind of review they do for PRs. But since this is a different beast it needs a different mindset during review.
And of course the only possible solution for that is proactice and active communication to get everyone on the same page.
What I've found is that there is no such thing as throwaway code if you share it with anyone.
I've had code I threw together in 10 minutes end up in production after putting it in a 'temporary' repo.
For a greenfield project that's fine - solve the issues that come up as they come. Someone is getting value right away.
Code you throw away makes the most sense to me for prototyping cross-cutting features in existing systems. A lot of companies can accumulate a ton of crud in only a few years of development time, and it follows Conway's Law that the crud is shaped like the org tree. Writing a throwaway feature end-to-end helps to identify the interfaces between the teams and subsequently components of the software.
This is also why I'm a big fan of assembling project teams over having teams "own" an area. Everyone on the project team is responsible for the project working well. Otherwise every team is worried about their small domain.
This is how green field projects become brown field projects in 4 weeks.
I don't agree. I believe that when the solutions' space is big design docs work best. If they are done correctly they are a (shallow) breadth-first search in the space. Specifically if in your design doc include "alternatives considered". Additionally, having multiple people looking at your doc you can prune some possible paths.
A breadth-first search is useful when you don't know much about the intricacies of the problem/topic at hand, and so you need some ground work to take informed decisions.
If, instead, the solution space is narrow then by all means do a prototype/throwaway PR/... to validate the approach.
Opinions sans reputation or data can still be helpful when they identify the landscape and issues, e.g.,:
A main goal of design is to identify what's not feasible through logic. So much easier than prototyping. But still "can it be done?" is a far cry from "can we do it?".
The benefit of prototyping is to show that something is possible. There the key question is integration: can persons Pn make code C on system S that meets both goal X and constraint Y - i.e., a design as product development.
Leaving aside bad-faith behaviors, the real question is socialization: 1. how to gather collective wisdom in some relatively natural and encouraging manner to encourage collective deep consideration of hard problems to avoid waste and get things done. And 2. the same for motivation: getting everyone aligned, understanding their role and the quality/schedule risks.
Given the uncertainties and costs of product development, the prototype has significant advantage of seeming "real" relative to some "ideal" solution, and thus can bias teams to the concrete, particularly when issues are contended. Prototype-driven planning likely reflects lack of organizational cohesion (and ironically kicks that can further down the road).
So generally I see prototyping as validating designs, and try to make the design discussions lean enough to engage otherwise-prototyping engineers.
Does anyone have a design doc format that they prefer for backend development? We’ve been working with ADRs, is that similar or the same as a design doc?
PS. I like the approach. Sounds like something I read before where devs would throw away their code everyday until they were satisfied with the result.
So we are basically re-discovering XP practices? (http://www.extremeprogramming.org/rules/spike.html)
I'm kind of on the fence about this approach. In the worst case you end up wasting time on a lot of dead ends. It's in such a situation that I kick myself for not spending time thinking about the problem before starting to write code.
The best times I've had with writing design documentation has been when I thought about the problem enough that I could specify the properties necessary for a correct implementation. Depending on the scope of the problem I might use property tests or model checking. In any case, thinking about the problem up-front tends to prune away dead-ends before you head down them.
(Of course, you can't do much about a bad specification except try to avoid them... learning how to write good ones takes experience, wisdom, and constructive input from knowledgeable colleagues).
Update: I’m on the fence because I’ve also had a lot of success with implementing partial solutions: just get something good enough that works for some use cases and improve as you go.
I think the key is to have a solid understanding that this is throwaway code.
I know of, erm, one quite company, fairly well known in its big industry, where indeed no one gathers any requirements and a junior guy is told to type it all in - with the view that it's probably 90% correct and the rest can be ironed out at some stage, or at worst rewritten.
But what tends to happen is thus:
1. The first draft tends to address all the wrong problems, doesn't abstract the right things, is over and under engineered in all the wrong places. Fine, it's a draft I guess
2. But here's the kicker: unless it's really unusable, there is now pressure on users to accept the draft, because changing it, perhaps substantially, requires work, and as it it sort of kind of works, if you squint. And this thing is there and you can pretend it's a job done.
I think ploughing in to writing code works if you ha r a decent idea of what you need to achieve and what the pinch points are. Without that it can be quite an expensive and frustrating approach.
I agree, to a point. Successful design docs require MORE work (or far more meeting time, at least) than rapid prototyping. Because they need to mentally step through all the logic, gotchas and integration issues without writing a line of code.
For a math metaphor, it's like doing a complex problem in your head then making a document stating which mathematical tools you will use, THEN writing down the actual formula. Versus the "show your work" approach of trying a few things, recalibrating or re-routing, then settling on the final approach.
So the design doc approach is truly terrible and inefficient. Except...
If a Very Smart Person is making the design doc and can pump out a perfect plan quickly...
And/or if the engineers implementing the solution are too junior to understand the build/rebuild approach...
Or if they are likely to miss various integration footguns...
Or if they are likely to go far down the wrong path completely...
Or if the organization is of such size that no one has understanding of all the parts and integration parts so you need to set hard I/O requirements for clean handoffs.
There may be more reasons for design docs, but I strongly agree they should only be used when necessary and certainly not required so that management gets to feel useful. They add a hefty tax to development speed and it's management's job to understand that cost/benefit ratio.
It think it mainly depends on the difficulty of the problem.
If you're working on a very difficult math problem - say, a novel proof - you're definitely going to lay out the tools and techniques, the various steps of the proof, before you sit down and start writing it out. But for homework problem #7 of 14, just start doing it - you'll figure it out.
Similarly, if you're writing a CRUD app in a stack you've used before, you can probably just start. But if you're writing something complicated that you haven't done before, you should probably visit the whiteboard before the IDE.
As they say,
> Build one to throw away (You will anyway)
(Probably first phrased "plan" by Fred Brooks)
It's always been the way I've coded, for better or worse.
I code fast prototypes though — throwing them out is easy. And when I kick off the 2nd iteration I have new insights going in.
(By the 3rd iteration I'm even hanging on to a few functions from the previous iterations.)
As Lamport says: the algorithm is not the code.
Not all products need an "algorithm" per se, but if your product needs to do collaborative editing or something else CAP-adjacent, that algorithm is going to need to live somewhere (not in throwaway code).
The right algorithm (or even if such an algorithm is possible) won't be discovered via iterating on the code base.
A lot of my code takes in garbage data and does some complicated stuff to produce useful outputs. Almost all the difficulty comes from hidden problems in the data upstream, including very often the data contradicting itself (a lot of fuzzy matching, 'voting' to determine the truth, etc.) None of these problems are ever-known beforehand, and can typically only be discovered after a lot of work has been done to make the data more intelligible (i.e. in the process of writing of my code).
I've realised over time that everyone else who prefers the design-doc approach refuse to do work like this. There's just a whole class of problems that are too hard without prototyping.
Right I feel like the problem isn't "prototyping is bad". Prototyping is good! But sadly many companies and devs aren't disciplined enough to throw away first solutions. And that's the real problem.
And its not just the companies, developers get too attached to their first solution, rather than using it as a way to discover knowledge. Companies need to reward knowledge discovery, not just "shipping to prod"
> Another important point is on using PRs for documentation. They are one of the best forms of documentation for devs.
This feels true, and also why design docs are important for projects that involve roles beyond software development, where PRs can be among the worst forms of documentation.
Being open to throwaway code before a design doc+review is an idea I promoted and tried to demonstrate in my projects at my most recent employer, but I found that it just didn't work within our engineering culture an expectations. Funnily enough, it had the most success when working on larger projects that spanned multiple teams and services, where often my team was consuming or providing an interface, and the code sample provided a clear usage pattern that other teams could critique or grok. I got the most dysfunctional comments within my own team on smaller projects and PRs (e.g. "not sure why you want to do it this way" on small code blocks, critiquing data structures used), so I'm still not sure if it was a company culture issue or team issue.
Aka “Ready, fire, aim” from the Pragmatic Programmer.
This approach is fine but make sure your iterative prototyping is refining some sort of e2e / acceptance test, with good comments / README describing the intent.
It’s really easy for the prototype to end up being shipped and painful refactoring happening once you’re successful.
For me, a lot of the time this ends up being a question of what is known vs unknown. If you have a clear customer requirement but technical uncertainty then iterating on prototypes is usually better. If you have unclear customer requirement then shipping MVPs iteratively is usually better. If you have organizational uncertainty (eg you need to compose components from multiple teams and align roadmaps, as is common in big companies) then the design doc is often the place to start.
I totally agree. The prototype being shipped is the #1 risk. And why even though I prefer this approach, it may be impractical if organizations can’t be disciplined.
If you have time to rewrite the prototype twice, you also have time to write and maintain the documentation which would have made it unnecessary to rewrite the prototype twice.
Not saying iterating the code is always bad, but it shouldn't be a substitute for sufficient up-front planning that would have saved programmer time.
And yes, the prototype usually ships. There is often not enough time to completely rewrite it, let alone fix all the bugs and add all the necessary features.
One of the challenges with this approach is answering "how much will it cost" and "how long will it take" - that being said, its often hard for non-developers to articulate exactly what they want without connecting it to something more concrete in the form of user interface.
Those questions aren't answerable in the first place.
I like the general idea behind ShapeUp: don't ask "how long will it take" but "how much time are you willing to literally throw in the bin to learn more about the problem". Committing to research is easy and HONEST. Committing to a finished product by a date is incredibly hard/impossible and a lie.
The first duty of all humans must be to truth.
It's quite rare to be in a scenario where not making timeline estimates is even an option.
* If you're B2B, your marginal clients are constantly deciding whether to sign or renew their contracts, and their money directly depends on promises you make them about when features they want will be available.
* If you have competitors, you have to evaluate how your product will match up with them over time. If there's some killer feature you could build with 3 engineers in the next quarter, you should probably do it. If there's a killer feature your competitors could build that fast, but you have some tech debt that would make it take 3 years, you'd better find a strategy to make that feature less important.
* If you have a large or growing product, you have more potential projects to do than people to do them, so you're going to have to decide which will produce the most benefits for a given unit of time investment.
If you have a small, non-contract-based project with no direct competitors? Sure, it's possible to get away with the "we'll finish when we finish" strategy. But there's not a ton of space in that niche, and unless you get quite lucky it's not where the big bucks are going to be.
I think this is largely a matter of habit, and not of reality. Yes, you might get the business short term if you lie and say some feature will be available Q3. But over time that type of constant lying erodes trust and hurts you.
I get it, you might need to do it to survive in the beginning because competitors will be doing the lying. But I'm saying you need to stop lying as soon as possible for the long term health of the relationship.
I didn't want to quibble about whether it's fair to call it "lying", but I think the term is misleading you. There's simply no such thing as a business relationship without time estimates. It's not a short term vs. long term or trust vs. distrust tradeoff - unless your business model consists entirely of off-the-shelf purchases, your customers will want estimates about what you'll do in the future and when.
Yeah, that sucks for management. Not my problem. ;-)
In fact more or less everything that I have found burdensome in the last decade or so of my career were things introduced to satisfy management.
Unit tests? Code coverage? For management peace of mind.
Code reviews? I'd argue, same.
Agile development (obviously).
Except, how long does it take to throw the code away? Sometimes never.
Write short designs; Goals, constraints, options. Prevents the tech debt from building over time and signals better when to throw chunks of systems away.
I don’t have link handy but one of the good talks that is posted on YT is about explaining how writing code is the „design phase” and not what a lot of people think „build phase”.
Build phase happens at the compile time and at runtime.
I have been moving towards integration tests as documentation approach, because in a fast moving environment anything that is not constantly verified has a tendency to become false.
How do you do that?
Im curious because I wrote a framework for this (same name as my username) but when I talk about the idea I receive a sea of blank faces.
Design docs are only as good as your knowledge about the problem you want to solve and knowledge about tools available for solving it.
If there are more unknowns than knowns imo its better to start with prototype to explore problem first and then eventually come back to planing.
Design docs are critical to your promotion. The boss and the committee do read your design docs, sometimes very carefully. Only your peers and at most your TL read your code.
> the committee do read your design docs, sometimes very carefully
Yadda yadda, that may be true sometimes but that the quality of the doc would get you promoted makes no sense and is also not a good metric. I think most of these committees look at who approved it and what known people thought/wrote in response. I was told in this promo-game to try to get comments from high-level people for this reason. That is more a popularity contest, and less a competition of well articulated ideas.
I think most people here want to improve the way things are. We talk about engineering practice to improve the practice, not to please management or give career advice. Usually, at least.
This is where it happens first. A bunch of engineers (or just generally people with boots on the ground) get together and find better ways to do things. Before agile was mainstream and corrupted beyond recognition, the waterfall model was the way recognized by management and working within that model would have gotten you promoted easier. Things evolve.
Prototypes discover constraints, docs document them (as well as what problem is being solved, what solution was chosen under those constraints and what are consequences).
Design docs are incomplete without reference implementation code. Which would require you to actually follow this process.
I wonder about all of the best inventions of all time and how many came from prototyping and how many were the result of a real peachy design doc.
But with without design docs, how will my hordes of low cost contractors know what to do?
almost every single time I've seen 'prototyping' done that was originally intended to be throwaway, it was ultimately the basis of the actual implementation.
Which is cheaper? A change in code? Or a change on paper?
A change on paper which needs to go though various approval stages, tasked up and finally implemented can be much more expensive than a change in code. Or the opposite.
I absolutely agree. And the other comments here have pointed out the danger of code is that someone will want to ship your draft.
So business processes can kill either one.
the idea that you can get value from just creating draft PRs without integrating them at all does not match my experience.
Preferring throwaway code over design docs (softwaredoug.com) is a Horrible take on software development.
I find myself researching and thinking a lot more about things before I actually program these days. It takes longer before I write, but my code is shorter and more to the point.
I don't think I'm personally capable of explorative coding. I get too lost in the weeds and lose sight of the problem.
If doing greenfield work, planning sounds nice, but if you're integrating with crusty / legacy systems that are poorly documented (if at all) planning is a waste of time; you'll end up saying "Library X will do Y" a lot only to find out none of that works the way you thought. So for brownfield, I may have a plan (written down or not) but it's mostly code (some throwaway) just to uncover how things actually work.
> Preferring throwaway code over design docs
Why writing it if you trow it away anyway ?
No. Just no.
Design docs should capture reasons.
Even if the code is throwaway, the reasons for the flow and process modelling for the use case don't change at the core. Some implementation details might change and the design may certainly also change accordingly, but the process is generally stable.
That's what should be captured in design docs.
I wish my employers would value throwaway code or design docs.
Lol at work i often merge prs with huge number if file changes as a result of treating it as a poc or draft first.
"Plans are worthless, but planning is essential".