Tuesday, March 31, 2009

Mercurial Branching

It's easy to do local branching in mercurial. My partner was exploring it for the first time and asked if this was supported in mercurial since it was supported in git. I didn't know, and dug into the the Mercurial book (pdf book) to find the answer. It's really very easy, though the use of update is a little odd.

You only really need the update and branch commands, but I include others here because they provide aid and comfort.

hg branch Name creates branch Name, and it becomes active on the next commit.
hg branch shows the current branch (no param).
hg branches shows the local branches.
hg update Name switches to the branch called Name.
hg tip shows you where you are wrt: branches, etc.
hg merge Name merges the named branch with the current one.

When you do a commit and allow mercurial to call up the editor, the commit message will be populated with the tip information, so you know for sure which branch you are in.

WAIT! these branches are not what you think they are. They are (as darrint said) permanent, and you can't really push to (or pull from) a separate repo into a temp branch. It's a very different concept from svn switch.

So be careful with that axe eugene.

Monday, March 23, 2009

Find Users of method through inheritor

So you have a method defined on an abstract base class, and you are going be messing with it. Someone decided to hang a deep inheritance hierarchy (deep to me means three levels or more) on the class. You want to know who is using an inherited method.

What G (Tim Gifford) did was pretty simple, but mighty handy. He just declared an override method on the abtract class's method, and used resharper's methods (show usages, show inheritors).

Quick intelligence, little real effort, and you delete the override method when you're done.

This is almost as nice as Dave Chelimsky's trick of commenting out a variable declaration in order to see where the variable is used. In VS.NET + Resharper, all the usages turn red. :-)

New Upgrade

Today we had a little upgrade to our internet access. We went ahead and moved from Vonage to Comcast for phone service, and included in the upgrade were a few cable channels and a boost in bandwidth. Channels: who cares. Bandwidth boost: yeee-haw!

I'm now 80% faster on downloads, and nearly 5x on the upload. I had an afternoon's worth of remote pairing with WebEx and Skype, and it went pretty well indeed. I'm sure it was just the emotional optimism on my part, but I like to think that the audio and video were smoother and that I had less lag with the keyboard.

I still have lag, and sometimes when my partner is refactoring the screen refreshes too slowly and I miss what he's done. Sadly, whomever is on the "hosting" side of things gets to do any work that has a lot of edits in a lot of places.

Some funny stuff, though. A change we thought was the "easy bit" took most of the day, and the ugly-seeming part took almost no time at all. I learned some new tricks. One trick was the 'introduce parameter' being cooler than I thought in Resharper (great job, JetBrains) and the other I'll blog separately.

It was a good day generally. We ranged around the code further than we expected, but left it in better condition. And our merges went really smoothly. It's not all that it could possibly be, but classes are shaping up quickly and we're cleaning like a white tornado. Watch that unit test count and test coverage rise!!

Now if we only can just get that feature done...

Caring About Stupid Stuff

A clear sign of a sick workspace is when people care far too much about stupid stuff, as reported at Tech Dark Side by David Christiansen.

Nicely done.

Contractor or Consultant?

Saying it here for future reference:

In the software world, the difference between a contractor and a consultant is whether you are paying for obedience or advice.


Reference: CIO Matthew Edwards' blog entry.

Friday, March 20, 2009

First glance: SPE and TDD

SPE is a pretty nice python editor. I'm using Ubuntu Intrepid Ibex's default version (0.8.4) and have had pretty good results most of the time. It has nice features like functional auto-completion, file navigation, debugging, UML generation, session history, shell window, tab and syntax checking, etc. Many of these features, sadly, I don't really need. I probably need a bigger project so I can really appreciate the UML and documentation generators. In today's little app, it hardly matters.

Overall, it is a very reasonable full-featured editor for python programmers.

SPE 0.8.4 suffers from the same malady many of the others suffer from: no real support for TDD. I can switch to a script and run it with Ctrl-R (after making sure I do a save on each file that has changed).

I would like to see the next version contain a Save-All feature and a nosetests runner. It would be even cooler if the nosetests runner would fire up automagically after a save or save-all or else invoke save-all on the way to running the tests. That would be so sweet.

I would give a more thorough review, but I'm learning the pmock library while learning spe, and that's taking more time from actual programming than I'd like already.

Power Of Naming

This is from a moderately recent copy of my Meaningful Names paper, and was translated into Java in the book Clean Code. Here it is in original context with python code examples.

The simple act of using a better name (instead of applying a better comment) can reduce the difficulty of working with the code we write.
What is the purpose of this python code?
             list1 = []
             for x in theList:
                     if x[0] == 4:
                             list1 += [x];
             return list1
Why is it hard to tell what this code is doing? Clearly there are no complex expressions. Spacing and indentation are reasonable. There are only three variables and two constants mentioned at all. There aren't even any fancy classes or overloaded operators, just a list of lists (or so it seems).
The problem isn't the simplicity of the code but the implicity of the code (to coin a phrase): the degree to which the context is not explicit in the code itself. The code requires me to answer questions such as:

  1. What kinds of things are in theList?

  2. What is the significance of the zeroeth subscript of an
    item in theList?

  3. What is the significance of the value 4?

  4. How would I use the list being returned?
This information is not present in the code sample, but it could have been. Say that we're working in a mine sweeper game. We find that the board is a list of cells called theList. Let's rename that to theBoard.
Each cell on the board is represented by a simple array. We further find that the zeroeth subscript is the location of a status value, and that a status value of 4 means 'flagged'. Just
by giving these concepts names we can improve the code considerably:
             flaggedCells = []
             for cell in theBoard:
                     if cell[STATUS_VALUE] == FLAGGED:
                             flaggedCells += [cell]
             return flaggedCells
Notice that the simplicity of the code is not changed. It still has exactly the same number of operators and constants, with exactly the same number of nesting levels.
We can go further and write a simple class for cells instead of using an array of ints. It can include an intention-revealing function (call it isFlagged) to hide the magic numbers. It results in a new version of the function:

             flaggedCells = []
             for cell in theBoard:
                     if cell.isFlagged():
                             flaggedCells += [cell]
             return flaggedCells

The flagged cells line is weird. It works, but what it does is create an immediate array containing just the one cell, and then add that array to the existing flaggedCells array. That’s funky. Why not use the append method instead?
             flaggedCells = []
             for cell in theBoard:
                     if cell.isFlagged():
                             flaggedCells.append(cell)
             return flaggedCells

That is much more obvious, I think. It’s a tiny change, but when we named the operation isFlagged(), the only uncomfortable bit is the way that we’re appending to the array. This is more straightforward, without changing much. It removes one temporary array, which is technically simpler.

But now it’s rather obvious that Python provides a much more terse way to write the code, and in this case it comes out rather clear and readable — and it eliminates the append() call entirely:
             return [ cell for cell in theBoard if cell.isFlagged() ]

Even with the function collapsed to a list comprehension, it's not at all difficult to understand.

This is the power of naming.  If it is this simple, and changes our code so much, what is a reasonable excuse for choosing poor names? 

Thursday, March 19, 2009

Probably something stupid...

I kept a mantra for my years in C and C++, and frankly I lost it when I was working in Python (probably a strong recommendation for the language). It is a useful and deeply wise saying, though.

You will someday find yourself in a little bit of confusion. A minute ago, the code was fine. You just made a very small change hoping for a very specific effect. Instead, the results you get are completely bizarre. Something deeply strange is happening, and it's not the effect you were looking for at all. The error messages are bewildering and seem totally unconnected with your change (especially if you're in C++ or Java).

Take a deep breath. Smile for therapeutic value. Close your eyes for a second, and look at your partner. Repeat the mantra:

It's probably something silly and syntactic.


This came about today, because we forgot to put a "virtual" on a method we were mocking. We've done this many many dozens of times in the past few weeks, but it's a manual step that you have to perform or your mock fails and the resultant error may be deeper in the code than you expected. You could make a list of all these things, but eventually you will find yourself digging in deeper than you need to.

Don't. Remember that it's probably something silly and syntactic.

I've had this over badly formatted templates, silly errors involving single v. double operator spellings (+ v. ++, = v. ==, * v **) and any number of silly typing accidents. Hey, and expert is just the guy who knows what all the mistakes look like.

Before you start scratching your head, chasing your tail, or pulling your hair out (as I must have) you should probably just recite the mantra and look at the change once again.

Oh, and because silly syntactic mistakes happen, don't let yourself have seven places or a half-dozen changes that might be wrong. You want to have one reason to be wrong, and you want to know right where it is.

If your partner did not catch the error, and the result is not what you thought you would get, relax. It's probably something silly and syntactic.

Do the Right Thing and Get It Done


Practice - Corey Haines from 8th Light on Vimeo.

Looking good, Paul P.

Rock on, Corey.

Windows wants to reboot

Sheesh. I'm trying to participate in a webex conference call, and windows wants to update. Then it wants to reboot. No, no, no. I have to get my cursor back from webex and click the darned "Reboot Later" box over and over.

Remoters, set your update appropriately! Who knew this would be a bother?

Article about readability of exception test

Readability and exceptions article by Bill Venners is worth a gander. I think his solution is elegant because it's obvious and terse. I love obvious and terse.

Wednesday, March 18, 2009

Web Cams

People told me that eventually Linux would bite me in the butt. I went a long time without that being true, or never being true in any serious way. I figured when it happened it would be something to do with drivers for some device.

Ubuntu Intrepid Ibex is the version that does the biting, and where it bites is the webcam.

Normally a webcam is nothing. If you're not doing a home "web cam show" or something, who cares? But now that I'm working from home, it matters quite a bit. I like to be able to see my partners sometimes and participate in meetings a little better. When audio is weak, reading lips a bit helps. But no.

Intrepid doesn't do webcams well. Especially Logitech QuickCam Chat models. Nobody has support for it.

I'm not rethinking Linux, but I'm rethinking Ubuntu. Maybe I need to go to Debian Testing and see if that works. Linux has many distributions, and their problems don't always overlap. Perhaps another choice will work better for me. I just hate to back up my computer and start over again. Maybe on the weekend.

In the meantime, I have ibex toothmarks on my behind.

Friday, March 13, 2009

Thursday, March 12, 2009

Clean Code Clarification

There is a confusing edit result in Clean Code on page 25, referring to class names:


Class Names

Classes and objects should have noun or noun phrase names like Customer, WikiPage, Account, and AddressParser. Avoid words like Manager, Processor, Data, or Info in the name of a class. A class name should not be a verb.


Read on its own, this makes it sound like I (or the editors) think "Manager", "Processor", "Data", and "Info" are verbs. Not so. Just an unwanted effect of editing. This is more disappointing than the neutering translation of my code example from Python to Java.

I became aware of the sloppy edit through a Sun forum where they were bashing the paragraph a bit, with good reason. Their arguments pro and con had little to do with my arguments, and I would have made clarification on the spot except they wanted me to to register and give a lot of personal information. Still, I suppose there should be a record somewhere of my correction.

Aside: if you want me to participate in your forum, don't make it a question of loyalty to your company or cause. I probably don't want you to have my phone number and home address and who-knows-what demographic information if that will take longer and have more lasting ramifications than my posting to your forum. And especially if it gives you spam powers.


That middle sentence is a reference back to page 21, where we talk about noise words and number suffixes. The problem with class names in the examples from the book is vague differentiation through noise words.

Most of the time, it is hard to tell the difference between several classes in a project which bear the names CustomerManager, CustomerGateway, and CustomerCoordinator, and CustomerHandler especially in the same project. What do the suffixes mean? Is a CustomerManager the class that creates and persists customers, or wouldn't that be the CustomerGateway following common naming practices? What is the Coordinator, and what does he coordinate with? And when do I use the Handler instead of the Manager? Words meaning "does something with" or "takes care of" don't quite cut it. Of these, 'gateway' has the most well-understood and widely prescribed meaning as a persistence mechanism. Differentiating between similar class names with noise-word suffixes is difficult. Much better if there is something in the name that clearly states its intent. Maybe one is a CustomerPersister, and another is a CustomerFactory and another is a TestCustomerGenerator.

Likewise, what is the difference between CustomerInfo, Customer, CustomerData, and CustomerObject? The additional words again provide differentiation, but without distinction. Is CustomerInfo the data-only persistent object I could use when I don't want to reify the Customer, or is it additional information about the customer relevant to an unstated aspect? Are CustomerData and CustomerInfo the same thing? Is one a message and another the Customer data formatted for a wire protocol? What is CustomerObject (and why put the word "object" in a class name?

I would rather the edit had not mixed the subject of verb-ness into a paragraph about poor noun choice. That was unfortunate.

Wednesday, March 11, 2009

Three Rules for Code Comments

Code comments are a topic which still draws a lot of heat (and an occasional burst of light) in programmer debates.

I understand.  If you are dealing with long, complicated functions with dozens of if conditions, many levels of indentation, and a number of design issues all at once then comments are a survival mechanism. They're absolutely an indispensable coping mechanism. Deleting them will make your code many times worse than it is already. In this regard, comments are a lot like the single-exit rule.

Also, like the single-exit rule, this coping mechanism is unnecessary when code is small and simple and everything is reasonably well-named.

Still, we find many comments that code would be better off without.

After spending time in debate, discussion, observation, and consolidation, my friends and I have come to the realization that there are only three important rules governing the use of comments.


The Primary Rule:
Comments are for ideas cannot be expressed in code. 

The Redundancy Rule:
Comments which repeat the code (add no information) must be deleted.

The Single Truth Rule:
If the comment says something that the code could say (via explanatory variables, functions, or even by better naming of existing entities) then the code must be changed to make the comment redundant (see rule 2). 

If we believe a comment is necessary, we examine it in the context of the code. Can we apply the Single Truth Rule to make the comment redundant (often via "extract method" refactoring or possibly with some renaming)? If so, then we invoke the Redundancy Rule and drop the comment.

Sometimes, we find that the Primary Rule is in effect. Perhaps we were forced by constraints to make an unusual choice (such as bubble-sort instead of quicksort, or an exhaustive sequential search instead of a binary search) and the code cannot express that algorithm choice easily. In that case the comment can remain. At some point, someone may come along and use refactoring techniques (perhaps renaming bubble_sort to slow_sort_in_constrained_memory).

This set of rules is also present with some good commentary at the Ruthlessly Helpful Blog and is included in the Code Smells album at Industrial Logic.

Remote Pairing Requires Headsets

When not in group meetings, remote pairing requires headsets on the in-house side. Otherwise, the voices of the partner tends to be no louder than the voices in the bullpen. Good noise rejection is pretty important in a headset.

The remote partner should keep the mute button handy. It's important when he wishes to hear all the sounds on the other side, or when the family is up and busy at home (unless he has an isolate office to work in).

I would recommend an office with a door, though I don't have one.

"called from" boolean variables considered ugly

One feature I hate to see in any code base (and have seen in several) is the "called from" argument to a function.

class EvilPoliticalRobotDouble: Minion {
...
public void runForElection(Person replicatedPerson, bool CalledFromRNC=false) { ... }
}


There are three different reasons that I hate this kind of "feature" and consider it harmful to clean code.

The first reason is that it doesn't even hint at what will happen if it is set true v. being set false. What thing will the code do differently? What decisions will it affect? Will it change the transactionality of the function? Behave in a less thread-safe manner? Will it be non-persistent? Will it choose a fast approximation over calculating an accurate answer? Will it choose different sources for the facts it acts upon? A different database? A different configuration file? Will it rely on different global variables (ick!)? Will it choose not to retry failed operations? What? What?? What?!?

The second reason is that there is something very wrong about a function knowing who its caller is. Shouldn't it do the same thing whether it is called from a test or a any of a hundred other places? If it changes its behavior, doesn't that make it a different function? It sounds to me like it is really two functions if it has a flag telling which function to use. That doesn't seem like clean code to me.

The third reason is that these things tend to cascade and pollute many methods on many objects. I've seen a number of functions that receive one or more "called from" indicators and pass them on to the called methods of subordinate classes. It may be that the function I'm calling with calledFromWebPage=true makes no distinction whatsoever. It may be that it passes it to a function which passes it to a function which passes it to a function that cares. How can you tell?

I consider the "calledFrom" parameter to be a naming smell at the very least, and a deep design fault at the very most. I suggest one eliminate such things.
  1. Trace the variable down and find out what it really does.
  2. Rename it so that it will make sense in tests.
  3. Wrap some tests around the true case and the false case.
  4. Look at using "extract method" to break out the variable=true from variable=false parts
  5. Try creating two paths, one for the 'true' case and one for the 'false' case.
  6. Alternative to 5: see if there's a different way to get the behavior a the lowest-level method, so that you don't have to corrupt the whole stack with 'calledBy' variables to get the effect you want.

Bounce Rates

It's a funny thing, but "bounce rates" at Agile In A Flash make me happy. According to google analytics, the "bounce rate" is the percentage of single-page visits. In the case of catalog or business pages, that is a bad thing often indicating that users did not find your site relevant. For Agile In a Flash, though, it more likely means that the user got the card he was looking for, probably from an RSS feed.

I would like to see more search engine traffic and a lot more comments, but I take the bounce rate more of a "customer satisfaction" rate. If you come for a card, and you get the card, you're happy. And if you're happy, we're happy.

Monday, March 9, 2009

Skype today

New tip:

Skype is more or less half-duplex so noise being picked up by the microphone affects your ability to listen to your remote partner. Adjust your audio settings. Don't let Skype automatically adjust your volume. Set the mic sensitivity down a bit so that your voice still comes through but rubbing your hands together, scratching your knee, or typing softly doesn't interrupt the conversation.

The other thing I found on Linux was that the volume setting for capture device was turned up all the way. If I didn't mute the mic, I couldn't hear my partners. I turned this device down and suddenly I have the behavior I wanted all along. I didn't know I had a "capture device" before. Watch for this!


Troubles de jure:


Finally gave up on skype around 11:08 today. Too many disconnects and audio trouble even when it was connected. Gizmo no better. Google voice & video really eats up my computer but is a more viable choice if we don't do anything but watch and listen on my side.

It's bandwidth. I ran some test from speedtest.net and I'm apparently getting only 6Mbps down, 1.3Mbps up. That's a new low for me. Guess I should add bandwidth testing to my morning routine. Switch from wireless to wire, same answer (actually slightly worse). Need a boost in bandwidth. I suppose I can talk to my ISP about that.

Talk to my partners, and see what I can do without A/V support.

Networking problem solved



This greeted me this morning with the cheery news that my network is not sick anymore.

Friday, March 6, 2009

Conference Calls and Remote Team Members

Today was a mixed bag. My code was surprisingly more performant than I expected once we moved a save point. The database handled my subselects better than I expected. I learned a bunch of stuff. I gained application perspective. That was all good.

The bad part of the day wasn't bad, so much as frustrating. We tried a conference call for planning. My audio was simply awful. It broke up so that I was missing phonemes from the middles, starts, and ends of words. The mic was not located close enough to all the speakers. I had to try to catch up from the context of people responding. Everyone was really quiet, except the other remote member who had a very strong signal (and thankfully a pleasant voice). It is an unfamiliar app, so I was not able to contribute much to the overall conversation and was a little stymied in trying to learn from the conversation.

One thing that bothered me was that my phone handset is not nearly as nice to work with as my computer headset. In the conference I was not hands-free.

I've always suspected that dialing into a meeting was almost like not being there. Now I know it's true. When people refer to "this" or "over there", you have to hope you can figure out what they're about. You're not visible in the room, so your expressions have no impact on the subject matter.

On the other hand, in my house the window is open and the weather is very nice. The computers are familiar and the bathroom is private. I have control over the brightness of the lighting and office space is my problem not someone else's perogative. There is a lot to recommend it.

So I guess what I need is a better way for us to do remote conference calls. I've tried it with computer A/V and with conference phone, and the computer a/V so far is much better. The only problem there was placement of camera and mic. I think I'll see if we can arrange an n-way call so that I can hear individuals better and maybe even see the room from various angles.

I'm interested in new ways. Time to go find some.

Thursday, March 5, 2009

Gizmo5

Today we realized that google voice & video was eating my laptop cpu. It's great, but a bit piggy. By the time you load up resharper, visual stupido and a good sized app, fired up a webex session, and fired up your VPN you find there's not much CPU left to do things like run unit tests and pull updates from subversion.

I decided to move the a/v off of my work laptop and use my personal Ubuntu-powered laptop for that. We tried skype first (which worked great) and then gizmo5.

Gizmo is cute. It has hold music (I like the 70s bit) and "blasts" (sound effects you can drop in your conversation) and does a fine job of keeping the voice sounding clean on linux and windows.

It doesn't have video in the linux version, but I am not having a lot of luck in Linux with my webcam with any programs. WengoPhone can sometimes use it. I think that Ubuntu Intrepid Ibex just hates the Logitech quickcams. It worked great before the upgrade (when resume from being suspended didn't kill the wireless). Maybe I should go back to real Debian.

Otherwise, I think that skype or wengo or gizmo should be fine for remote pairing. Gizmo is just cuter than the others. Just for fun, I put the laptop with gizmo on the old network and the WebEx screen sharing and vpn on the other. It worked pretty well.

Didn't get so much work done today. Early in the week we were making some real progress, but every day later in the week seems to be filled with network troubles and tool issues or windows/browser trouble. My network was solid today, but laptop CPU was an issue.

Well, it's always something.

XCopy still works

Just so we all can remember (or find) this later: xcopy in windows has a /Z option to resume in case a file transfer fails. For you WFH types who are stuck in windows, you might save a lot of trouble by using this flag when moving from your remote office server/nas/workstation to home computer over vpn. This is especially useful if the network is spotty.

Wednesday, March 4, 2009

Home Networking

If your home network isn't quite up to snuff, it will put quite a damper on your pairing velocity. You will be doing a lot of VNC and VPN and screensharing and file transfers. If your normal bandwidth isn't everything you want it to be, just wait until you lose wifi and all your sessions have to be re-stablished. It can take several until you can continue where you left off.
Today :
  • I had a download of a scrubbed production database over secure VPN fail multiple times.
  • The method I used for downloading the file did not have a "resume" feature.
  • I had video and audio either freeze up, "stammer", or "flutter" all morning and afternoon.
  • I had my WebEx screen session drop out on me a few times.
  • Twice I had my laptop suddenly drop off the home network. I was in the middle of a quick architecture meeting and dropped out for probably fifteen minutes.
  • I lost time to a short series of reboots because windows was acting all wonky with relationship to the network. It didn't start normally after the first reboot. It's likeley that the problem was hardware responding to network troubles, but wonky is wonky and windows loves a reboot (oh, yes it does).

I made a number of changes to improve my situation:

  • The wireless G router I had set up earlier was in a corner of the house, and my office is on the opposite wall of the room opposite the office. I set up a second router, positioning it on top of a tall bookcase closer to the center of the house. I think it's better. I see better connection strength anyway. I do notice that the download speeds are more along the lines of 9Mb/s (have seen over 10) and the upload is closer to 1.5Mb/s.
  • The old router was also near a wireless phone base. I hear that's generally not such a good idea. Not so the new one.
  • The new router has shiny new firmware, because I updated it immediately.
  • I tried moving the older router (which is also a Vonage phone appliance) to be further down the chain, so that I got my wireless first. It made no discernable difference, and I feared degradation of phone service so I put it back. I'll leave it as-is. It did teach me that my modem is specifically expecting a certain MAC address. With that, I grew a new admiration for the standard feature of setting a soft MAC address on appliances.
  • Most of my neighbors (and I see networks for a lot of neighbors) are on 1, 6, and 11. Sigh. I don't have a lot of hope of avoiding collision, but since most of them are channel 1, I shifted up to a higher channel.




I wish I'd paid more attention to my home network before getting into this. I will continue the home pairing, but I really do need to have this stuff rock-solid.If you want to provide pointers to wifi stability tricks, please feel free to post comments and/or links.
I could use the help.

Tuesday, March 3, 2009

Continuing remote pairing & SQL testing

This week has had a lot of network issues. We've temporarily gone voice-only to reduce our bandwidth. We're screen-sharing and voice chatting and hoping for the best. Yesterday was awful, today not so much.

We also ran out of free trial for TeamView and LogMeIn (both excellent) and are resorting to WebEx for screen sharing. WebEx works pretty darned well, except that the scroll wheel on any mouse I own sends me immediately to the bottom of the file. Yikes. We also continue to have trouble with colors. I see very low-contrast foreground/background combinations (white on light blue, yellow on white, blue on black etc) but the remote partner sees different, more reasonable colors. These two problems together are a constant dripping faucet.

OTOH, the partner is great and we were able to make some progress between stepping on ORM landmines.

We test-drove our changes through the API/Business layer, but needed to get our gateway working. We use the strategy of creating a transaction in setup and rolling back in teardown. We've been moving along pretty well, until we hit a problem with NHibernate many-to-many relationships. Given classes X and Y with a many-to-many relationship, I can add objectX to objectY and then query objectY back from objectX easily enough, but objectY doesn't know that it has an objectX. We ended up taking research breaks. In NHibernate, one of the classes "owns" the relationship, and the other simply does not. This was not fun. In trying to remove all X for a given Y, it is recommended that we query all the Ys that contain an X and delete it from each. That sounds about half-mad when you have an RDBMS. Luckily, it does the right thing in SQL when we do this, but it's a little mad. It's more hoops to jump through.

A take-away lesson is that the ORM is "something that might break" or at least "something that might be broken." If we hadn't gone to the lengths to test the ORM, then we would have been left with non-working SQL under a well-tested layer of business objects.

Second computer is a pairing smell. I struggle against its siren song. It distracts me and I have to catch up. Maybe it's ADD or something, but I can't keep my hands/mind/eyes off of it. This afternoon, no more second computer. I'll take notes on paper.