Ask HN: Bottom Up Programming Question

16 points by awt ↗ HN
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 ] thread
The Design Recipe from How To Design Programs is very bottom-up based.

Personally, I'm against HtDP because it strips out all of the romance in programming for me, but it does work.

It isn't a question of "only top-down" or "only bottom-up". Of course, some thinking will have to go in from both directions. This is why, personally, I think the directed-tree metaphors don't really 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.

Another great metaphor I find analogous is "Mozart vs. Beethoven" style programming

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.0: Has a beak, which can open and close, but only eats green bugs. If you attempt to use it on a blue bug it crashes. Unfortunately, no eyes.

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.

...

And if you think that is at all unrealistic, you're talking to the guy who wrote a program to print bingo cards whose 1.0 version, among numerous other faults, could not accommodate any printer but the default one ;)
Nah, that's pretty much the tale of a lot of software that I've written.
The only thing missing from some development goes is a notable feature reversion based on manager deciding that it is now going against the strategic vision.

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

You're an okay guy, but please stop mentioning bingo software constantly in almost every post you make. It's getting quite annoying. We know you write a bingo software, but please stop marketing it all the time. It's like being at a party and there is a mattress salesman who always brings the conversation back to mattresses and how he got started, and how he found good deals and so on. At the party, one can walk away from that guy, here, unfortunately not. So, don't use this forum as a marketing tool for your bingo thing. Just talk normally, and if people want to know more about our product, they click your name.
My apologies, that wasn't my intention. For one it would be rude. For two it would be stupid -- it is highly probable that precisely none of you are in my market.

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?

Also factor in the times you say stuff like "in my business", or "in my area" without explicitely mentioning your software. Such statements seem to be you begging us to check your profile to see what your business is.

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.

Lighten up. He wasn't marketing. He was illustrating his point with his own experience.
I'd love to, but I love my donuts!
Your analogy seems to me to go awry here:

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

Your duck example is interesting. Obviously the evolution doesn't use any top-down design.
Nice but you have to program the cells to contain the appearance of the duck the cells should know about and because that is a feature difficult to grasp it does not matter where you start. First design the beak put it in the cell Design the webbed feet put it in the cell Design the head put it in the cell Construct all cells and enumerate the cell based on the ducks feature And there you have it bottom up designed DUCK with out worry where to start.
What you look for first is always a unit that functions on its own. Then you program that. For example, a feather could be used in a feather bed, or it could be used in a duck. You can program a feather, test it and make it work, without needing to have designed or modelled a duck yet - of course, you'll have an idea where it belongs, but since it is an enclosed and reusable component, it can be created first without a top down model.
Hmm... I thought this was going to be about dynamic programming based on the title.
(comment deleted)
Isn't it more a matter of means vs ends. Top down is more or less means programming in which you don't really know where you are going but know how you are going to get there. Bottom up is more or less ends programming in which you don't know where you are but know how you are going to get there. Seems to me you need a good bit of both to get from where you are to where you need to be.
The biology analogy immediately starts you off on the wrong foot. That has nothing to do with code.

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

> So, once again the question, how can bottom up programming work without some level of top down design?

Ever heard of something called... LINUX!?

You're always thinking top-down, even if "top" is just the next level up, as patio11's example shows.

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.

It's not that bottom up is good - it's that top down is simply impossible. Try to design a duck this way. It sounds very sensible, and you'll get a sketch in about two minutes. Not detail it: a couple of hours and 5 pages. Now continue to do this until you no longer have to backtrack: i.e. until what you discover about the duck in the current level of detail does not change at all what you have designed so far.

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.