Ask HN: Bottom Up Programming Question
One thing that I have never quite grasped about the concept of bottom up programming is how to come up with the most basic components of a program without doing some kind of top down analysis. Perhaps I can illustrate my confusion better with an analogy.
Let's say you're designing a duck (in other words, an animal that can fly, has a rounded beak suitable for bottom feeding, webbed feet for swimming, etc). In a Bottom Up approach, you would start with cells. You would need to create skin cells, bone cells, eye cells, nerve cells, etc. In order to even get to this stage, however, you would first have had to have divided the duck up conceptually into different parts in order to know that different cell types are needed. Thus it seems impossible to do bottom up programming with out first doing some top down conceptualization.
So, once again the question, how can bottom up programming work without some level of top down design?
27 comments
[ 3.6 ms ] story [ 78.0 ms ] threadPersonally, I'm against HtDP because it strips out all of the romance in programming for me, but it does work.
A much better way of thinking about "bottom-up design" is exploratory programming - where one decides what to write by writing it [1]. This means you start by discovering and solving sub-problems, by creating components which work in their own right, documenting and unit testing them (if you're working in an environment that needs it), and moving on to solving bigger problems using said components.
On the other hand is the "architecture approach" - where you measure twice and cut once. By "measure", I mean decide what all of your components will be, write out UML diagrams for them, figure out your types, your interfaces, and the contracts between them, write unit tests, and only then begin to write run-time code.
There are much more experience programmers than me who advocate both ways, so it's hard for me to make a call [2].
Another great metaphor I find analogous is "Mozart vs. Beethoven" style programming: http://stackoverflow.com/questions/292682/understanding-dijk...
I like this one because suggests that you can make "great" programs either way. I use the quotes, because obviously, music is a matter of taste, as is the quality of software we develop. This is important to remember - for some people, Windows is the perfect OS out there (for example, those concerned with Microsoft's profits over the OS's usability).
1. http://arclanguage.org/
2. Although the ones who advocate exploratory design seem more well-rouded: Alan Kay, Paul Graham, and the 37signals guys come to mind. Since I want to be well-rounded and not a specialist, I try to follow in their example. Obviously, this is a subjective judgment.
Wow, that's really interesting. I find Beethoven to be an incomparably greater composer than Mozart, and I feel the same way about the exploratory approach to making software: it's just obviously better. Whether it is obviously better or just a better fit for me is a fair question. I will say, though, that I don't feel the same way as you about the advocates of the top-down, up-front style. What have they built? What systems did Dijkstra build? As far as I'm concerned the great practitioners are pretty much all lined up on one side. Counterexamples are welcome.
(I should add that I don't mean to belittle Dijkstra's achievements, just that as far as I know they didn't lie in the direction of building working software. Of course that didn't stop him from castigating basically everyone who did do that.)
Duck 1.2: Eyes.
Duck 1.21: Eyes now see color.
Duck 1.22: Eyes now see colors including black. Apparently, some bugs are black. Damn users and their damn uncolored colored bugs.
Duck 1.5: Realized eyes and mouth both share a lot of code. Refactored as "nerves". Allowed consumption of turquoise bugs -- not exactly sure why they're safe when blue bugs are not, oh well. Added quack just for fun. Spent 2 weeks getting the quack to sound exactly right.
Duck 2.0: Now with feet and wings! Turns out to have feet you really need muscle tissue. Refactored again, stealing code from Mouth. Supported modes of operation include walking, running, and swimming. Option in menu entry for flying but if you push it the duck dies.
Duck 2.1: It glides!
Duck 2.11: So it turns out that to utilize the gliding feature users were driving ducks off of tall cliffs. This resulted in downward acceleration being sufficient at end of glide to overly stress feet muscles, causing critical duck failure. Added in Bones. Damn users and their damn cliffs. As long as I had Bones I implemented them in the Mouth too, can finally eat blue bugs.
Duck 2.12: Some blue bugs are poisonous?! WTF kind of bugs do you have in Australia. Added in Tongue. Borrows heavily from existing Nerve classes. More refactoring.
...
"Duck used to have webbing in his feet, but MGMT thought having another fluid-moving apparatus would diminish how much you value the wing. May be available as a paid add-on at a later date."
I am, typically, neither rude nor stupid.
I am also typically surrounded by people who care very little about startups, software, and the like. So when I'm around other people who care about software, startups, and the like, I tend to mention our point of commonality a bit. The one I bring up most often is my own because a) I'm intimately acquainted with it and b) unlike my day job, I can talk about it without getting fired if I say too much.
But I try to be a good citizen here. You may feel that I mention my software in almost every post. Memory plays funny tricks with us. I don't trust mine with anything important, so I checked with the indispensable external memory of searchyc.com, and it turns out that I've mentioned it about 18 times in the last 255 comments, which is about 7% or so. (Throw another 6 on the tally for other people referring to my software, if you care to.) Now I might be counting wrong, as I suck at it almost as bad as I do at remembering things, but I don't suck quite so badly that 7% is "almost every post".
(Sidenote: this is my 256th comment? There should be a geekiversary for that.)
Now I do try to keep my contributions to the community worth having me here. If you are to believe the last ~1800 ish times people pushed the "More comments like this one please!" button, I like to think I'm doing a halfway decent job. But I'll try to recalibrate due to your feedback.
Say, what would it take to get you behind the controls of your very own gliding duck?
Maybe just 7% of your comments are about your business, but imagine you are talking to a mattress salesman, and every 10th sentences he says is about mattresses or about his business. Is that not a bit annoying?
I'm not saying what you are doing is wrong or that it does not fit the standards of the community. I'm just saying it annoys me personally. Nobody else maybe, just me. So I'm pointing it out to you, because it may also be annoying someone else, and if nobody mentions it, you won't be aware of this.
For example, you original comment on this thread was funny and insightful. The followup comment about bingo software was not, it just seemed like marketing.
btw, duck can't fly so u may want to cut that feature
you would start with cells - sounds good
you would first have had to have divided the duck up conceptually into different parts in order to know that different cell types are needed - does not follow.
I noticed that your premise assumes a requirements analysis that already nails down what the program is supposed to do. While that way of thinking is common in the corporate world (where, I note in passing, effective software development is not common - coincidence?), it doesn't fit well with the style of programming you're asking about. If you've read PG's piece on the subject (http://www.paulgraham.com/progbot.html) you may recall that there's one passage in it that he emphasized. It begins:
It's worth emphasizing that bottom-up design doesn't mean just writing the same program in a different order. When you work bottom-up, you usually end up with a different program.
The program changes as it evolves and your understanding of what the program is changes as it evolves [~]. All sorts of interesting feedback loops result.
When I work this way, I listen to what the code is telling me. That is, I start with something primitive (your "cells" in the analogy) and sense where it's leading. Perhaps it's akin to riding a horse where there's a balance between what you want to do and what the horse wants to do; or to creating a piece of art where the creative process incorporates feedback from the medium itself and from the work as it's emerging.
That doesn't mean it's all spontaneous, with no guiding factor. If that were true, no system would emerge - you'd just keep going around in circles and finding yourself back at the primitive level. But the guiding factor is not at all like "dividing the duck up conceptually into different parts". It is more like having a felt vision of what a duck would be like. You have that vision and you keep referring to it as you go along. But the vision itself clarifies, and sometimes changes, in response to what happens in the code. You discover what the system is as you make it.
Learning to listen to what the code is telling you is one of the most interesting things you can do as a programmer.
([~] PG's piece doesn't say that your concept of what the program is changes, only that the constructs it's composed of will be different. I think the two are related though.)
In code, a function to trim whitespace from a string is perfectly functional on its own. When you actually try to write code this way, a rather large number of things can be written, larger than you may think.
Rather than write a lengthy explanation, my suggestion is to write a non-trivial program in Haskell. After that you'll better understand how it is possible to have many functions that can stand on their own, and I think this will help clarify the situation in your mind.
You can do this in any other language, but most languages make it relatively easy to reach out and touch lots and lots of state. Real-world evolved systems tend to do the biological equivalent all over the place, to a level even the worst spaghetti code ever written doesn't even come close to aspiring to, and do not resemble human-engineered code in the slightest. The Haskell advantage here is that it won't let you. Achieving this in other languages right now is a chicken-and-egg problem; if you knew how to do that you wouldn't be using the metaphor you cited.
(BTW, just to state it explicitly: No, I didn't directly answer your question. As others point out, it's not a very informative question. I'm going for the root of the problem, how you are conceiving of functional units in a program.)
Ever heard of something called... LINUX!?
I'd start with the "outside" of the program and work inwards. This forces you to categorize features as greatest to least important.
Refactoring can be significant -- much more than top-down. When used poorly, this can lead to the "design a 747 by building a wheel first" mentality, however. In other words, like everything else there's a place for it in the toolbelt.
You know, you can always model the problem domain first and still code bottom-up. The structure of the data is probably the most important factor anyhow, no matter how you code or what the qualities of the language are.
This will have two effects: first, the complexity of the design is equal to the complexity of the duck. That's A LOT of paper. And second, most mistakes in the design will cause the duck to fail spectacularly.
On the other hand, with bottom-up design you start with something which eats and reproduces, and improve it constantly until it has a beak and feathers and quacks. It will certainly not look anything like a duck at fist, but it is very likely that from the second or third iteration it will solve 80% of the client's problems. Sometimes even from the first, but that's just luck. It's messy, but it's at least doable.