Jon Aquino's Mental Garden

Engineering beautiful software jon aquino labs | personal blog

Thursday, July 26, 2007

Searching for the simple view that makes sense of the mass

Effective domain modelers are knowledge crunchers . . . They try one organizing idea after another, searching for the simple view that makes sense of the mass. . . . This distillation is a rigorous expression of the particular knowledge that has been found most relevant.

– Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software

Does this describe you? It certainly describes me ("searching for the simple view that makes sense of the mass"). I'm loving this book so far (available on O'Reilly Safari Online).

Friday, July 20, 2007

On the Dynamics of the Perception of Time

Every morning I try to write three pages in my diary. I've noticed that the third page always takes less time to write than the first page. It reminds me of "time dilation" (the apparent slowing down of time) from Einstein's theory of relativity. Might there be something analogous here? When you wake up, your brain is coming up to speed, so your first diary page will be done somewhat slowly. Then, as your brain warms up, the second page will be done faster, and finally, when you are fully awake, the third page will be done fastest of all. But to you, your writing speed seemed to be constant throughout. Time seemed to slow down for you in the morning.

We might expect the opposite to happen in the evening: time speeds up. So as your brain starts shutting down in the evening, things take longer for you to do, though you don't notice it. Outside agents like clocks and TV shows seem to go by faster. In other words, time seems to speed up.


Formalizing the Nature of Perceived Time

Let's call the speed of perceived time s. In an empty room with no windows, s is constant. If you add a window or a clock, time will seem to slow down in the morning and speed up in the evening. That is, as your ability to think improves, s decreases; when your mental capacity drops, s increases. One might suppose that s = 1 / k, where k is mental capacity.

This explains the old saying that "time speeds up as you get older". As your mental capacity gradually decreases (k↓), the perceived time will speed up (s↑). Conversely, when you are a child, your mental processes are at their fastest (k↑) and time seems slow (s↓). Recall the long days of elementary school.

But this isn't the whole story; grown-ups can have long days as well! And what about the observation that "time flies when you're having fun"? Is this not experienced by young and old alike?


Resolution to the Anomaly

When your mind is engaged in something fun and stimulating, you devote most of your mental capacity k to that activity; your brain is too busy to check outside agents like clocks and TV shows very often. Because these external markers of time are checked with less frequency, long periods of time will elapse between each reading, and thus time will appear to be moving quickly. We can generalize our earlier result to

            s = a / k

The speed of perceived time s increases with mental activity a but decreases with mental capacity k.

Thus, "time flies when you're having fun" (s↑) because of the increased mental activity (a↑). Conversely, if your workday is extremely boring, your mental activity will be low (a↓) and time will seem to go by slowly (s↓).

s = a / k has some interesting implications for the manipulation of perceived time. If you are in the middle of a lecture that you find boring and you want to speed up the time (s↑), try sleeping (k↓), i.e., decrease your mental capacity. If you find you've been missing deadlines consistently and want to slow down the time (s↓), try learning, to increase your overall mental capacity (k↑).

Want your vacation to go by more slowly (s↓)? Do as little activity as possible: sit in a chair and stare at a corner of a room (a↓). On the other hand, if you visit Las Vegas, your mental activity will likely be highly stimulated (a↑) and time will speed up (s↑).

Thursday, July 19, 2007

Debugging: Lessons Learned

Here are some lessons learned from that debugging book I mentioned a few posts back.


Understand the System
- Read all related documentation
- Draw a system diagram and understand how things are connected
- Know the capabilities of your debugging tools


Make It Fail
- Start from a clean initial state
- Consider automating lengthy steps
- Make it fail in situ; don't waste time simulating the environment
- For intermittent bugs: list possible factors and try varying them one at a time; output a logfile and look for patterns


Quit Thinking and Look
- Watch it fail
- Use Remote Desktop / VNC
- Add logging and monitors
- Don't start thinking until you've limited the number of possible causes


Divide and Conquer
- Binary search
- Use test data with an easily identifiable pattern
- Start at the failure point and work backwards
- If you discover other bugs that may be related, fix them before continuing your search


Change One Thing at a Time
- Don't panic
- Back out changes that have no effect
- Compare the logfile with that of a good system
- Check earlier versions


Keep an Audit Trail
- Keep a detailed written log


Check the Plug
- D'oh!
- Have the components been properly initialized?


Get a Fresh View
- Try explaining the problem to someone (or something)
- Ask an expert: co-workers, the vendor, documentation, bug database, the web
- Report symptoms (including possibly unrelated observations), but not your theories


If You Didn't Fix It, It Ain't Fixed
- Fix the root cause
- Make the problem happen again by undoing your fix

Monday, July 16, 2007

Toward a unified theory of social networks

"It gradually became clear that bits and pieces of a unified theory were surfacing, but that a crucial element capable of tying them all together in a quantum-mechanically consistent manner was missing."
– Greene, The Elegant Universe

Is this not an accurate description of the current state of the web?

Friday, July 13, 2007

How to find people (and blogs) like you, using LibraryThing

This is a way to find people like you by correlating their books with yours.

1. Go to LibraryThing.com and input all your books
2. Go to your profile page and find the Members With Your Books box
3. Check out each member to see if they have a blog (the Cooliris Previews firefox extension can make it easier to check out each link)

I found 10 new blogs this way - looking forward to them.

Tuesday, July 10, 2007

Books on my radar screen

  • Algorithms, by Sedgewick. Actually I'd rather read Knuth, but Sedgewick is available on Safari Online. Plus Knuth is expensive and I hear bad things about the fictional MIX language he invented for this book.
  • Why Programs Fail. On debugging.
  • Beautiful Code. Great sample essay by Tim Bray.
  • Code Craft
  • Debugging by Thinking. Maybe. Not sure from the reviews whether this one is for novices or whether pros can benefit as well.
  • Debugging: The Nine Indispensable Rules. Great war stories.
  • Microserfs. From Joel Spolsky's reading list.
  • PPK on JavaScript. May not be useful to an experienced JavaScript developer, but hey it's on Safari Online.
  • Programming Pearls. C-based but evidently has some gems.
  • The Elegant Universe. Seems to be a "Short History of Nearly Everything" for string theory.
  • The No Asshole Rule

Books I should finish:
  • Rapid Development. Good catalogue of software project management techniques.
  • Mastering Regular Expressions
  • CSS Mastery
  • The Principles of Beautiful Web Design. Alas, this full-color book is a bit boring.
  • Agile Software Development (Robert Martin)
  • Refactoring to Patterns
  • Streamlined Object Modeling. Doesn't seem useful to me though. Wonder if Domain-Driven Design would be better - I hear that one's a bit verbose unfortunately.
  • Facts and Fallacies of Software Engineering (Safari Online). Occasional intriguing observations about the software development process, but I'm not sure how useful they are.
  • ActionScript The Definitive Guide. Depends how much Flash work I will be doing.

Monday, July 09, 2007

Isomorphic Bookmarks (for books)

Most bookmarks simply won't do. They let you know roughly which page you are on (but is it the left page or the right?), yet they don't tell you which line you stopped at. Here I will describe how to construct, out of ordinary household materials, an isomorphic bookmark which uniquely identifies the line on which you stopped reading.

The construction of an isometric bookmark is straightforward:
  • take an index card and cut it in half
  • draw a line from the center to one of the corners, as illustrated below
Camera 710
Fig. 1. Isometric bookmark

The isometric bookmark has the following property: For any line L on page P, there exists an orientation θ that uniquely identifies L on P.

In other words, by turning the card you can have it point at any line on any page – left-facing or right-facing, near the top, middle, or bottom, as shown below.

Camera 711
Fig. 2. Bookmarking a line near the top of a left-facing page.

There are line-based bookmarks like LastLine that you can buy at bookstores, but it's hard to beat the isomorphic bookmark for simplicity, cost, and structure-preserving topological invariance.

Camera 712

Sunday, July 08, 2007

Notes on debugging and programming: Softpanorama

"While [writing new software] may be a deeply satisfying occupation, hunting bugs can be just as absorbing, rewarding and fulfilling. So don't feel intimidated or bored when it is your fortune to fix a bug."

From the Softpanorama pages, which is a great collection of wisdom about programming and debugging, and some great book recommendations.

Rite-In-The-Rain notebooks for shower idea capture

I have previously recommended Crayola Bathtub Crayons as a useful means of recording ideas in the shower. However, it can be quite hard to read, and on more than one occasion I have been unable to decipher what I have written. Plus it discolors the shower walls slightly.

What a nice surprise, then, to find that my local art store carries Rite-In-The-Rain notebooks.

camera 706

These are water-resistant notebooks that are perfect for capturing ideas in the shower. You don't have to spend for a waterproof pen – pencil works fine. The pages aren't totally waterproof; if you really soak them, the coating wears away and the page goes transparent. So I recommend that you stick it behind a shampoo bottle.

A Rite-In-The-Rain notebook will set you back $8.

camera 705

Saturday, July 07, 2007

[Windows Freeware] UltraExplorer (replacement for windows file explorer)

I think I'm in love. Just trying this out, so it's too soon to say, but this freeware file manager has received good reviews in alt.comp.freeware. It's called UltraExplorer.

2xExplorer was OK but it limits you to 5 favorite folders.

This one has a Mac-like "column view" (see below). Dual panes if you want them. Also a handy command prompt at the bottom.

SnagIt Capture

Thursday, July 05, 2007

Language Independent Visualizer (LIVER)

How many times have you come into a software project facing a source-code tree that you are unfamiliar with, and said to yourself, "Man, I wish I could visualize this thing". But of course there isn't a visualization tool for PHP, JavaScript, ActionScript, etc.

Until now. This 100-line Ruby script will examine your source-code tree and generate a .dot file that you can visualize using any GraphViz viewer. I like ZGRViewer because it does anti-aliasing, although it is a little cumbersome to use. (In ZGRViewer, be sure to try the "fdp" tool - it's better than the "neato" tool because it does clustering).

I call it the Language Independent Visualizer, or LIVER for short. It works with Java, PHP, JavaScript, ActionScript, and – with a little modification – pretty much any language in which you can identify the top of a function block using a regular expression.


JavaScript:

Image-0080


Java:

Image-0081


PHP:

Image-0078


ActionScript:

Image-0079


The Ruby script is below. Simply change the $source_directory at the top, then run it: ruby liver.rb

$source_directory = 'C:\p4b\xnapps\bazel\flash-sources\slideshow\src'
$file_granularity = true # true for file-level granularity; false for function-level granularity

#
# Extracts function names from all files in $source_directory
#
def parse_function_names()
require 'find'
Find.find($source_directory) { |path|
next if File.ftype(path) != 'file'
basename = File.basename(path)
line_number = 0
open(path).each { |line|
line_number += 1
name = function_name(line, line_number, basename)
if name
put_in_set_map(name, basename + '@' + name, $name_to_functions)
put_in_set_map(basename, name, $filename_to_names)
end
}
}
end

# @todo Make a SetMap class [Jon Aquino 2007-07-06]

#
# Adds the value to the set map at the given name. A set map has as its
# values arrays with unique elements.
#
def put_in_set_map(name, value, set_map)
set_map[name] = [] if ! set_map[name]
set_map[name] << value
set_map[name].uniq!
end

#
# Returns the array of values for the given name, or an empty array if none exist
#
def get_from_set_map(name, set_map)
return set_map[name] ? set_map[name] : []
end

#
# Builds a map of function names to function names referenced
#
def parse_function_references()
require 'find'
Find.find($source_directory) { |path|
next if File.ftype(path) != 'file'
basename = File.basename(path)
current_function = nil
line_number = 0
open(path).each { |line|
line_number += 1
name = function_name(line, line_number, basename)
if name
current_function = basename + '@' + name
next
end
next if ! current_function
tokens = line.split(/[^a-z0-9_]+/i)
tokens.each { |token|
next if get_from_set_map(token, $name_to_functions).length != 1
next if token == current_function.split('@').last
put_in_set_map(current_function, get_from_set_map(token, $name_to_functions)[0], $function_to_referenced_functions)
put_in_set_map(get_from_set_map(token, $name_to_functions)[0], current_function, $referenced_function_to_functions)
}
}
}
end

#
# Returns the function name in the given line of code, or nil if
# it is not the first line of a function definition.
#
def function_name(line, line_number, basename)
if $file_granularity
return line_number == 1 ? basename.sub(/\.[a-z]+$/i, '') : nil
end

if line =~ /function\s+([a-z0-9_]+)/i # PHP/JavaScript
return $1
elsif line =~ /([a-z0-9_]+)\s*:\s*function/i # JavaScript
return $1
elsif line =~ /([a-z0-9_]+)\s*=\s*function/i # JavaScript
return $1
elsif line =~ /(public|private|protected)\s+[a-z]+\s+([a-z0-9_]+)\(/i # Java
return $2
end
return nil
end

#
# Converts the function name into a unique alphanumeric name
#
def node_name(function_name)
if ! node_name_exists(function_name)
$node_name_index += 1
$function_to_node_name[function_name] = 'Node' + $node_name_index.to_s
end
return $function_to_node_name[function_name]
end

#
# Returns whether the function has been assigned a node name
#
def node_name_exists(function_name)
return $function_to_node_name[function_name]
end

#
# Removes functions with too many or too few connections
#
def purge_functions
$filename_to_names.each { |filename, names|
names_to_purge = []
names.each { |name|
inbound_connections = get_from_set_map(filename + '@' + name, $referenced_function_to_functions).length
outbound_connections = get_from_set_map(filename + '@' + name, $function_to_referenced_functions).length
if inbound_connections + outbound_connections == 0 or inbound_connections > 10
names_to_purge << name
end
}
names_to_purge.each { |name|
names.delete(name)
get_from_set_map(filename + '@' + name, $function_to_referenced_functions).each { |referenced_function|
get_from_set_map(referenced_function, $referenced_function_to_functions).delete(filename + '@' + name)
}
get_from_set_map(filename + '@' + name, $referenced_function_to_functions).each { |function|
get_from_set_map(function, $function_to_referenced_functions).delete(filename + '@' + name)
}
$function_to_referenced_functions.delete(filename + '@' + name)
$referenced_function_to_functions.delete(filename + '@' + name)
}
}
end

# Colors from http://colorbrewer.org [Jon Aquino 2007-07-06]
colors = ['8DD3C7', 'FFFFB3', 'BEBADA', 'FB8072', '80B1D3', 'FDB462', 'B3DE69', 'FCCDE5', 'D9D9D9', 'BC80BD', 'CCEBC5', 'FFED6F', '7FC97F', 'BEAED4', 'FDC086', 'FFFF99', '386CB0', 'F0027F', 'BF5B17', '666666', 'DEEBF7', '9ECAE1', '3182BD', 'EFF3FF', 'BDD7E7', '6BAED6', '2171B5', '08519C', 'C6DBEF', '4292C6', '084594', 'F7FBFF', '08306B', 'D8B365', 'F5F5F5', '5AB4AC', 'A6611A', 'DFC27D', '80CDC1', '018571', '8C510A', 'F6E8C3', 'C7EAE5', '01665E', 'BF812D', '35978F', '543005', '003C30', 'E5F5F9', '99D8C9', '2CA25F', 'EDF8FB', 'B2E2E2', '66C2A4', '238B45', '006D2C', 'CCECE6', '41AE76', '005824', 'F7FCFD', '00441B', 'E0ECF4', '9EBCDA', '8856A7', 'B3CDE3', '8C96C6', '88419D', '810F7C', 'BFD3E6', '8C6BB1', '6E016B', '4D004B', '1B9E77', 'D95F02', '7570B3', 'E7298A', '66A61E', 'E6AB02', 'A6761D', 'E0F3DB', 'A8DDB5', '43A2CA', 'F0F9E8', 'BAE4BC', '7BCCC4', '2B8CBE', '0868AC', '4EB3D3', '08589E', 'F7FCF0', '084081', 'E5F5E0', 'A1D99B', '31A354', 'EDF8E9', 'BAE4B3', '74C476', 'C7E9C0', '41AB5D', '005A32', 'F7FCF5', 'F0F0F0', 'BDBDBD', '636363', 'F7F7F7', 'CCCCCC', '969696', '525252', '252525', '737373', 'FFFFFF', '000000', 'FEE6CE', 'FDAE6B', 'E6550D', 'FEEDDE', 'FDBE85', 'FD8D3C', 'D94701', 'A63603', 'FDD0A2', 'F16913', 'D94801', '8C2D04', 'FFF5EB', '7F2704', 'FEE8C8', 'FDBB84', 'E34A33', 'FEF0D9', 'FDCC8A', 'FC8D59', 'D7301F', 'B30000', 'FDD49E', 'EF6548', '990000', 'FFF7EC', '7F0000', 'A6CEE3', '1F78B4', 'B2DF8A', '33A02C', 'FB9A99', 'E31A1C', 'FDBF6F', 'FF7F00', 'CAB2D6', '6A3D9A', 'B15928', 'FBB4AE', 'DECBE4', 'FED9A6', 'FFFFCC', 'E5D8BD', 'FDDAEC', 'F2F2F2', 'B3E2CD', 'FDCDAC', 'CBD5E8', 'F4CAE4', 'E6F5C9', 'FFF2AE', 'F1E2CC', 'E9A3C9', 'A1D76A', 'D01C8B', 'F1B6DA', 'B8E186', '4DAC26', 'C51B7D', 'FDE0EF', 'E6F5D0', '4D9221', 'DE77AE', '7FBC41', '8E0152', '276419', 'AF8DC3', '7FBF7B', '7B3294', 'C2A5CF', 'A6DBA0', '008837', '762A83', 'E7D4E8', 'D9F0D3', '1B7837', '9970AB', '5AAE61', '40004B', 'ECE7F2', 'A6BDDB', 'F1EEF6', 'BDC9E1', '74A9CF', '0570B0', '045A8D', 'D0D1E6', '3690C0', '034E7B', 'FFF7FB', '023858', 'ECE2F0', '1C9099', 'F6EFF7', '67A9CF', '02818A', '016C59', '016450', '014636', 'F1A340', '998EC3', 'E66101', 'FDB863', 'B2ABD2', '5E3C99', 'B35806', 'FEE0B6', 'D8DAEB', '542788', 'E08214', '8073AC', '7F3B08', '2D004B', 'E7E1EF', 'C994C7', 'DD1C77', 'D7B5D8', 'DF65B0', 'CE1256', '980043', 'D4B9DA', '91003F', 'F7F4F9', '67001F', 'EFEDF5', 'BCBDDC', '756BB1', 'F2F0F7', 'CBC9E2', '9E9AC8', '6A51A3', '54278F', 'DADAEB', '807DBA', '4A1486', 'FCFBFD', '3F007D', 'EF8A62', 'CA0020', 'F4A582', '92C5DE', '0571B0', 'B2182B', 'FDDBC7', 'D1E5F0', '2166AC', 'D6604D', '4393C3', '053061', '999999', 'BABABA', '404040', 'E0E0E0', '4D4D4D', '878787', '1A1A1A', 'FDE0DD', 'FA9FB5', 'C51B8A', 'FEEBE2', 'FBB4B9', 'F768A1', 'AE017E', '7A0177', 'FCC5C0', 'DD3497', 'FFF7F3', '49006A', 'FEE0D2', 'FC9272', 'DE2D26', 'FEE5D9', 'FCAE91', 'FB6A4A', 'CB181D', 'A50F15', 'FCBBA1', 'EF3B2C', '99000D', 'FFF5F0', '67000D', 'FFFFBF', '91BFDB', 'D7191C', 'FDAE61', 'ABD9E9', '2C7BB6', 'D73027', 'FEE090', 'E0F3F8', '4575B4', 'F46D43', '74ADD1', 'A50026', '313695', '91CF60', 'A6D96A', '1A9641', 'FEE08B', 'D9EF8B', '1A9850', '66BD63', '006837', 'E41A1C', '377EB8', '4DAF4A', '984EA3', 'FFFF33', 'A65628', 'F781BF', '66C2A5', 'FC8D62', '8DA0CB', 'E78AC3', 'A6D854', 'FFD92F', 'E5C494', 'B3B3B3', '99D594', 'ABDDA4', '2B83BA', 'D53E4F', 'E6F598', '3288BD', '9E0142', '5E4FA2', 'F7FCB9', 'ADDD8E', 'C2E699', '78C679', '238443', 'D9F0A3', 'FFFFE5', '004529', 'EDF8B1', '7FCDBB', '2C7FB8', 'A1DAB4', '41B6C4', '225EA8', '253494', 'C7E9B4', '1D91C0', '0C2C84', 'FFFFD9', '081D58', 'FFF7BC', 'FEC44F', 'D95F0E', 'FFFFD4', 'FED98E', 'FE9929', 'CC4C02', '993404', 'FEE391', 'EC7014', '662506', 'FFEDA0', 'FEB24C', 'F03B20', 'FFFFB2', 'FECC5C', 'BD0026', 'FED976', 'FC4E2A', 'B10026', '800026']

$name_to_functions = {}
$filename_to_names = {}
$function_to_referenced_functions = {}
$referenced_function_to_functions = {}
$function_to_node_name = {}
$node_name_index = 0
parse_function_names()
parse_function_references()
purge_functions()
purge_functions()
purge_functions()

puts 'digraph Dependencies {'
$n = 0
$filename_to_names.each { |filename, names|
next if names.length == 0
color = colors[$n % colors.length]
if $file_granularity
puts ' ' + node_name(filename + '@' + names.first) + ' [label="' + filename + '", style=filled, color="#' + color + '"];'
else
puts ' subgraph cluster' + $n.to_s + ' {'
puts ' node [style=filled, color="#' + color + '"];'
names.each { |name|
puts ' ' + node_name(filename + '@' + name) + ' [label="' + name + '"];'
}
puts ' label="' + filename + '";'
puts ' }'
end
$n += 1
names.each { |name|
referenced_functions = get_from_set_map(filename + '@' + name, $function_to_referenced_functions)
referenced_functions.each { |referenced_function|
puts ' ' + node_name(filename + '@' + name) + ' -> ' + node_name(referenced_function)
}
}
}
puts '}'

TimeSnapper (Windows), and the 9 Rules of Debugging

Trying out TimeSnapper, which is a free Windows program that automatically takes a screenshot every 5 seconds, every 5 minutes, every 5 hours, whatever you like. I've got it set to every minute (with the limit set to 3 GB). This is part of my grand plan to create a memex (lifetime store of everything). Other components: Google Desktop for searching email, calendar, tasks, files, past webpages; daily webcam photos; Emsa personal keylogger; chat/IM logs; Evernote.

In other news, David Agans' Debugging: The Nine Indispensable Rules looks to be an entertaining read. Here are his nine rules:

SnagIt Capture

Wednesday, July 04, 2007

Webcam as Memex

@Computer

I am intrigued by Vannevar Bush's 1945 idea of a memex, a "lifetime store of everything". I wish I had a device built into my eyeglasses that recorded everything I see and hear, everything I type (for instant retrieval later), every website I visit, every email, every phone call I receive. It's often useful to be able to go into the past and recall what you were doing, or retrieve a document or email from the past. And it's just fun to see pictures from a week ago, a year ago, a decade ago.

Well I don't have a wearable memex, so I've set up the next best thing which is a webcam that takes my picture automatically every 6 hours. It reminds me of Mena Trott's project to take a picture of herself every day for the rest of her life. Or Project 365, in which you snap a photo every day. The beauty of this setup is that it's done automatically, so I don't need to remember to do it.

image

Sunday, July 01, 2007

O'Reilly's Safari Books Online - handy service for reading technical books online

Currently reading on O'Reilly's Safari Books Online: Facts and Fallacies of Software Engineering. An entertaining book on software-development pitfalls. 4 stars on Amazon.

I'm growing to like O'Reilly Safari Books Online. Each month, for $20/month, you can read 10 titles online (from O'Reilly, Addison-Wesley, and some others). I like it because it's cheaper than buying the book, and I don't have to wait for the book to arrive. Downsides are that they might not have the book (I was looking for "Debugging by Thinking" and "Debugging: The Nine Indispensable Rules", but it had neither); also it's more comfortable to read hardcopy rather than off your computer monitor.

But the immediacy is hard to beat. If there's a book I want to read, I check if Safari carries it, and if so I can start reading immediately. The YubNub command to search for titles is saft, e.g., saft PHP Cookbook.