

Ask HN: How do you think about programs? - GeneralMaximus

I've been trying to learn Apple's Cocoa libraries for a few weeks now. I found that writing small applications is a piece of cake, but when I start writing larger apps I tend to get lost in the complexity of my own creation.<p>My question is this: how do you think about your applications while you're writing code?<p>Hillegass uses object diagrams (is that what they're called?) to represent relationships between objects in his apps. How do <i>you</i> do it? More importantly, do you use a pen and paper to design your programs up-front (my preferred method) or do you jump right into the code? Do you use diagramming tools to create your object diagrams? Do you write specs? Do you write notes to yourself?<p>What I'm trying to find out here is an effective method for creating a high level overview of the app I'm working on so that I can tape it to the wall and look at it to figure out what I have to do next. My usual method of writing down TODOs in random notebooks and losing them at college doesn't seem to be working out that well.
======
frig
This is cocoa-specific, though some of it generalizes.

I'm not a huge fan of the Hillegrass book for actual beginners, but sadly
there's not a better book out, yet; it's almost easiest to learn some other,
better-documented GUI application framework, then come back to Cocoa (assuming
you have time).

Assuming you don't have that kind of time, though, here's my recommendations:

Cocoa development also depends heavily on the workings of NSApplication, and
it's a good idea to stop and read up on how that works; it's super helpful to
actually know what's going on between the points where the code you wrote is
being used, and to understand that you need to have a working understanding of
how NSApplication is structured.

Apple has ok docs on this (I'd start by reading up on NSRunLoop, then looking
up anything you encounter you don't understand), but nothing amazing.

Once you've gotten that understanding, you'll have a much easier time
understanding what you have to do to make an application do what you want.

Now we move on to handling specific applications.

I suggest the following design algorithm:

"Simultaneously", start figuring out your application's user interface and its
data model. I say "simultaneously" but it's really a bit of a back-and-forth.

For example, you decide you're going to write a blogging client (I think this
is an example in Hillegrass's book?).

So, ask yourself what data items does a blog post typically have? Provisional
answer: title and body.

What user interface elements do you need for a blog post? The data model
dictates those: right now, a text field for title and a larger text field for
the body.

If we were going to be more realistic we'd probably want a richer data model
(some "authored by" field, some field for "tags" or "categories", etc.) and a
more-complicated user interface to match.

If you hit a "stopping point" in this process -- meaning, a point @ which you
are satisfied that your data model covers everything it needs to and your user
interface elements are complete -- we can move on to the next step.

The next step is getting a sheet of paper, or a whiteboard, etc., and putting
up some representation of each user interface (eg: each type of window or
dialog), with lots of space between them.

Next, you go through each user interface item and ask

\- "what information does this item need to display itself properly"? (eg: a
blog-post-editing view needs the blog post's data object.) You should annotate
each user interface item with some representation of the data item(s) it needs
to render itself.

Once you've done that, you make another pass through each user interface
element, asking the question:

\- from this user interface item (say, the blog post editing window), which
other user interface items can I get to? Which other user interface items can
I be arriving from?

Once you've done that, you go back through and ask the following question for
each "entry path" into a user interface item:

\- for each entry path into this user interface item, what information would I
need to set up this user interface item correctly? (for example: if you're
editing an existing post, you need to have the data object corresponding to
the post you're editing; if you're creating a new post, maybe you need
nothing, or maybe you need some kind of placeholder reference to the post-to-
be-created)

Note this is DIFFERENT from the step where you listed the data item(s) the
user interface item needed to render itself. For example, to render itself,
the blog-post-editing-view needs SOME blog post data object; however, if
you're moving from a list of all blog posts to editing a specific blog post,
the blog post editing view needs THAT SPECIFIC BLOG POST.

Once you've done that, you make a final pass, asking the following question
for each "exit" path:

\- for each exit path from a user interface item, can I supply the information
the user interface item I'm entering needs? (ie: if you're leaving the "blog
posts" listing view to go edit a blog post, the blog-post-editing-view needs
the blog post's data object; can you supply it?)

For example: if I'm leaving the "blog post listing view" and heading into the
"blog post editing view" to edit a specific blog post, then the "blog post
editing view" needs to be given that specific blog post to render.

Is it possible to get that information from the "blog post listing view" to
the "blog post editing view"? Yes, b/c the "blog post listing view" knows
which "blog post object" you selected.

You'll find yourself answering "no" when you have what I'd call a long-
distance dependency in your user interface workflow: to get to view C from
view A you pass through B, but the naive implementation of B doesn't let you
pass along some needed bit of information from A to C; this is probably the
single-most-common source of frustration/head-slapping when you're first
learning Cocoa (or any GUI app).

Hopefully, you'll finish all this work and have found no unpleasant surprises
(ie: you have exhaustively analyzed your program's workflow using the above
information and all information you need can be delivered to where it is
needed).

If you've got some gaps -- user interface modes that can't get information
they need -- you should fix your diagram (adding extra data-items to user
interfaces as-needed).

Once you're done with this you should more or less have the "Model" and "View"
tiers of your application figured out -- the "model" objects are all the data
model objects, and the "view" tier is all the user interface items you've made
(eg: all the separate .nib files).

You can go ahead and write the data model classes and create the .nib files
now.

From here, building out skeletal controllers is pretty easy: for each user
interface item, you build out a controller class, making sure it has fields
for each user interface element in your user interface item (eg: it has one
field each for the "title" text field and the "post" text field) and one field
for each data item it has to know about (eg: a "post editing view" controller
has a field for a "blog post" data object).

You now have to add the actual logic to the controller. This comes in three
main forms:

\- you add code to synchronize the user-interface with the data item (ie: if
I've got this "blog post" data item, write a method that makes the on-screen
"title" field == the "title" field in the "blog post" data item, and so on)

\- you add code to synchronize the data model with the user interface (ie: if
the on-screen "blog post" has such-and-such title and such-and-such post, this
code makes the "blog post" data item's fields the same)

\- you add code for handling each "entry" and "exit" from the corresponding
view (eg: one method for setting up a view to start editing an existing blog
post, one method for setting up a view to start creating a new blog post, and
some methods to handle closing-the-view-and-saving-your-work)

If get through with all the above, you're going to be pretty close to finished
with whatever application you're writing; the last steps are dealing with
stuff like the "startup" code (when the program starts, how does it bootstrap
itself into a user interface state?) and similar tasks.

The above process is extremely heavyweight, and I don't pretend to use it
anymore -- I might sketch out particularly-complicated parts, but not the
whole app -- but I do recommend mocking up at least your first few progs with
it; it's a good learning exercise, and it's a little more "obvious" than a
pure "object diagram".

------
lallysingh
I've tried a lot of systems (omni* and a few others), but here are what stick
with me:

Emacs Org-Mode for the day-to-day stuff that I want to keep in front of me
when I'm writing code. It's essentially perfect for it. Using the tables with
pasted-in code is utterly fantastic (hint: replace-regex '^' with '|> ' and
it'll keep your indentation before you hit tab and tablify the code).

Org mode does tables, outlines, todo lists, agendas, mostly everything a
programmer's going to want day to day.

For higher-level planning, I leave the workstation. Get a cup of
coffee/soda/juice/who-are-we-kidding-its-coffee and a table away from
computers. A moleskine (the squared one, with the grid layout) and a good pen
(I like these $4 pilot extra-fine vballs) is what I use. Relaxing music in
noise-canceling headphones FTW.

Do yourself a favor and begin with the habit of writing the date on a page
when you start writing on it. It's useful for you to see your progress over
time, and it can be helpful later in patent disputes.

As for the thinking process itself, here's my heuristic:

\- Small apps are mostly driven by a framework you just use (probably some
MFC/DI garbage). Keep your code clean & organized, and work a little to keep
the expected chaos from working with always-mysterious frameworks contained.
E.g. don't ever trust the framework to handle your model code for you. Use
proxy classes, multiple inheritance, whatever to firewall yourself from that
chaos.

\- Medium apps have enough internal structure to get past the cognitive mass
of the framework. There, you end up with a top-level abstraction that
structures the app. Be it a pipeline, a set of command objects, or a data
structure you maintain, there's one big one and the app is structured by it.

\- Large apps have a few of these. They're independent subsystems and you
firewall their interactions the best you can. Unless you've got the time/labor
for large-scale refactoring, you generally just try to keep the abstractional
dams from breaking. In those cases, I usually try to get some middleware in
there, so I can easily handle stubs, testing, and interactive use with each.
If you've got a REPL, you can avoid it, but in C++/Java, it can really help.

~~~
lallysingh
Edit: s/MFC/MVC/. Freudian slip there.

------
pbrown
I started with post-it notes, but kept losing them too. Now I use Freemind,
and have a really big whiteboard (of the manual variety) on the wall in my
office.

You mentioned you're having a problem with big apps. I'm the same way. At the
risk of sounding condescending (which I'm not) break the big app down into a
bunch of small apps (features). I find this works for me in two ways. 1.) I
can code the small apps easier (and then ideally put them together into the
big app). and 2.) If I find I'm getting feature creep, it's easier to dump 1
or 2 "small" apps rather than rethinking the whole project.

------
karim
"Divide each difficulty into as many parts as is feasible and necessary to
resolve it."

René Descartes

I break the program in functional parts that are easier to grasp and I
assemble those parts only after having written tests for those parts.

------
rubentopo
No one method works all the time, this depends on the paradigm of the target
language. At least i can't use one method.

One thing that makes it much easier (at least for me) is trying to use
dependency injection, otherwise i get lost.

Misko Hevery said it better. [http://misko.hevery.com/2008/11/11/clean-code-
talks-dependen...](http://misko.hevery.com/2008/11/11/clean-code-talks-
dependency-injection/)

------
jacquesm
I outline first, then turn the outline into the program. Items on the outline
are 'actions' or 'data', data goes to the top of the outline, actions follow.

I hope that's clear and that it will work for you, it works like a charm for
me, even for very large projects.

Outline editors are a dime a dozen, not sure which ones you could use on the
mac though...

------
lyime
Omni Graffle and Mindmaps. Pen and Paper works too.

