Archive for May, 2007

Capture error messages as text

Tuesday, May 29th, 2007

Here's a hidden Windows tip that I just learned: hit Ctrl+Insert instead of Alt+Print Screen when you get an error message. This will copy the message to the clipboard as text rather than as a screen shot. Now you can paste it straight into Notepad on your way to the Google search box. This is a great tip to share with those to whom you provide tech support.

AiS 27: Mapping, Scope, and Partial Order

Tuesday, May 29th, 2007

Listen Now

On the SSG oven software project, Raymond and I used several mathematical truths to prove that the design met the stated requirements. Three of those truths are mapping, scope, and partial order.

A mapping is a function from one set, called the domain, to another set, called the range. The range is not larger than the domain. If the mapping is one-to-one, then a reverse mapping exists and the range is exactly the same size as the domain. A hash is a many-to-one mapping, so the range is smaller than the domain. Therefore, no reverse mapping exists.

A scope is a context in which simpler objects reside. You can abstract the relationships among the enclosed objects as relationships among the scopes in which they live.

A partial order is a transitive relationship between objects in a set. It is not as restrictive as a full order, but allows for some independence. A partial order, because it is transitive, must be acyclic. Replacing a full order with a partial order can relax the constraints on a design, and give you room for optimization.

Your homework is to design an Integrated Circuit designer. Allow the user to build a component out of other components. Prove that your design does not allow the user to create a recursive cycle.

Table names should be singular

Thursday, May 24th, 2007

The Rails convention is for database table names to be plural. The Handmark convention was previously the same. However, our new DBA (Chris Simonton for those of you who listen to the show) changed that convention to singular. The reason for the change lies in a common misconception about database tables.

Most people think about a table as a collection of rows. The database is a place to store data, and the data is organized in rows. The rows live in the tables. Seems pretty straight forward.

But that line of thinking leads to some serious mistakes. A collection is a group of things that all belong together. Like a group of employees who work for the same company, or a group of line items on an invoice. But a table contains rows that are completely unrelated to one another.  The employee table contains employees of various companies, and the invoice line item table contains information about several invoices.

Thinking about the table as a collection of rows leads to an assumption that the rows are somehow related. We might assume that all employees in the Employees table work for the same company, or that all of the rows in the LineItems table have a common vendor. After all, the application will be hosted by that company or that vendor, right?

Applications may start out in one scope, but they tend to outgrow their initial design. If we allow limitations to creep in because of inadvertent assumptions, it may be difficult to make the jump when we need to. Assuming that there is a relationship among the rows of a table can cause you to forget foriegn keys. After all, if every employee works for the same company, why would you need a CompanyID column in the Employees table?

We made that mistake at Radiant. The Enterprise product hosts data for restaurant companies. Since the product started out as a targeted system for a small number of customers, we made the assumption that each company would have its own database. As a result, none of the tables contained a CompanyID. The database name itself identified the company.

In the days when a new company signed on about once a month, it was no big deal to prop a new database from the model. But as the product grew, we were adding companies more frequently. And as we made changes to the product we had to deploy those changes to each company individually. But because of the assumption, we were unable to merge companies into a common database when we recognized that it would be a good idea.

A database table is not a container. It is a type. It is not a collection of rows, but a collection of columns. It defines not a set of data but a set of relationships. It is alalogous to "class Employee", not to "private List<Employee> _employees". To remind us of this, database names should be singular.

AiS 26: The Case for Proven Design

Tuesday, May 22nd, 2007

Listen Now

If you’ve taken a course in analysis of algorithms, then you’ve learned how to prove that an algorithm will have a certain result. You’ve proven that quicksort will terminate, that it will actually sort the list, and that it will complete in n log n time. But in the real world, all of the sorting and searching algorithms are already written. We no longer need to prove algorithms. Now we need to prove designs.

Cycle of discovery
Programmers tend to work in a code-test cycle. We’ll write some code, then we’ll see if it does what it should. Then we’ll go back and change the code and test again. The cycle continues until the feature is complete. This is a cycle of discovery. This is how we explore new things. This is the scientific process. This is not how other engineering professions work. Sure, it gets the job done, but it’s not the most efficient way to do it.

Agile development techniques put new labels on the cycle of discovery. Now instead of code-test we have red-green-refactor. But it’s still the same cycle. We don’t know ahead of time that the code we are about to write is correct. We just keep typing until it works. In this world you don’t know how close you are to the finish line until you cross it. How are we ever to estimate our work? Or mitigate risk?

Would you buy a house from someone who says, “let’s just keep nailing boards together until it looks like a building”? Of course not. But most software is more complex than a house. It takes longer to build. It has more moving parts. It’s more expensive. We should be doing at least as much as a home builder to ensure that we will deliver the right product, at high quality, on time, and on budget.

We can do that by proving our design.

Cycle of proof
In my day-to-day work, I follow a different cycle. I’ll learn the requirements of a feature, design the feature, code it once, and then fix bugs. I don’t spin in a tight code-test cycle. Once I know what the design should be, I just sit down and write it all out. Sure, I may have bugs and typos, but they tend to be easy to fix. I may go days between compiles, but in the end I know that it will work. I know because I’ve proven it.

Record Transactions, not Balances

Friday, May 18th, 2007

The typical example of an atomic transaction is a financial transfer. You want to make sure that you reduce the balance of one account by $100 and increase the balance of another account by $100 atomically. It is incorrect for one to occur without the other. And so, the example shows us, you wrap these two actions within one atomic transaction.

The problem is, this example is completely bogus. Banks don't work this way. They record individual transactions, not account balances.

I learned this lesson the hard way when I worked on the eCard product at Radiant Systems -- a refillable gift-card service. Storing account balances is a Bad Idea. Here's what can go wrong.

In order to adjust an account balance stored directly in the database, you have to issue two SQL statements: SELECT the current balance, perform the calculation, then UPDATE to the new balance. (Sure, you could do this with the one statement "UPDATE SET balance=balance-100", but then you couldn't check for overdrafts.) Even within a database transaction, there is the possibility that the record is changed between the SELECT and the UPDATE. This could happen any time the transaction isolation level is set to something lower than the most paranoid "serializable". To fix this, raise the level, or apply a query hint to the SELECT statement, such as "for update".

Whichever solution you choose, you have just increased the locking in your database, and decreased concurrency and scalability. And we have seen that occasionally, even these tight restrictions don't work as advertised. There are still some incorrect balances that we can't explain to this day.

When you have a distributed system, such as Radiant's eCard in caching mode, you always have to bring the transactions together at some point and verify correct balances. This is the same process that you go through each month when you balance your check book, only at a larger scale and with greater automation. Reconciling balances is simply impossible. You need to identify and compare individual transactions (think check numbers), and add missing transactions to each list.

When things go wrong, you often need to go back through the transaction history to see where it diverges. If you don't have this history, then you obviously have a problem. But even if you just keep a log in parallel with a balance (as we did at Radiant), you will have a hard time determining exactly which of those transactions was applied incorrectly.

The best practice is to keep a running list of transactions by account. Occasionally, you can snapshot and archive those transactions, like an accountant closing the books each quarter. Then to find the current balance of an account, apply all recent transactions to the snapshot. This solution is scalable, distributable, and auditable.

Find your kernel of certainty

Tuesday, May 15th, 2007

We have entered the vicious stack of crises. This is the cycle of issues that follows the most-recent top-priority law. This stack is not strictly LIFO, however. If the PM receives a call regarding an issue further down the stack, it is escalated to the top.

Our stack is currently three issues deep.

The new features are a week late. But they have to slide because the fix that we pushed to production on Friday didn’t work. We told our customer that the problem would be solved before the weekend, so now they are breathing down our necks for an estimate on the real fix. But we can’t work on that now because the new build is bringing down the QA servers and they can test.

It is in times like this that I look for my kernel of certainty. Instead of tackling the chaos head-on, I need to start from an island of sanity. I need to find one piece of the system that I can trust implicitly. From there I can reach out to other components and bring them under control.

At the moment, I can’t trust MySQL. Two of these issues may be related to the database itself doing something unexpected. I need to bring something else to bear on the problem. Unfortunately, I can’t trust Log4J right now, either, because it wasn’t accepting my configuration changes on the live production server. So I have to retreat one step further. Right now my island consists of Java, Eclipse, MyEclipse, and JBoss running on my own box. And JBoss is in danger of being voted off.

Once I pare the world down into its most sane parts, I can start to extend my reach of confidence out to Log4J. When that is again within my control, I’ll bring that to bear against the MySQL problem. With visibility that I can trust, I may find that I solve all three crises simultaneously. However, I won’t find a solution by attacking each issue directly, with their priorities shifting, using tools in which I have no confidence.

XmlSerializer Correction

Saturday, May 12th, 2007

I mentioned in episode 23 XMl Inside Out that there was no attribute in .NET that would flatten a collection. Raymond and I stumbled upon just such an attribute accidentally.

The issue was that all legs of a flight were direct sub elements of the "Flight" element. Serializing an array puts all of those legs under an intemediate "Legs" element. However, if you use the attribute [XmlElement("Leg")] instead of [XmlArray("Legs")], you will get a flat collection.

The point is still valid, however, that you will find some schemas that you cannot match with XmlSerializer. It was designed to be really easy, not really flexible. These two goals are often at odds with one another.

AiS 25: Case Study: SSG

Wednesday, May 9th, 2007

Listen Now

Raymond and I have been working on a time crunch project for the past week. We take a break from our design work to discuss the project so far. We've been fortunate to work with an experienced team of professionals -- SSG -- who understand how to make a softawre project succeed.

SSG pulled together an exhaustive requirements document. This document is an outline-numbered list of line items, each one describing one functional aspect of the system. The requirements document gives the team a common set of goals from which to work. It doesn't explain exactly how each of these functional requirements are met. That job is left to the user experience designers.

SSG hired a user experience design team to construct a conceptual model of the system and translate that into a series of wire frame designs. A wire frame shows the visual components that comprise a system, but don't attempt to make those components attactive. For that, SSG has hired user interface designers.

The user interface design team is skilled in graphic design. They provide the artistic talent to make the user experience look good. That leaves Raymond and myself on product development. We are primarily responsible for making the system work.

SSG was seeking a parallel workflow between user interface design and product development. They chose to use Windows Presentation Foundation to separate those two processes. However, Raymond and I decided that the introduction of such a new technology to such an urgent project would add too much complexity. So we advised that we use Update Controls instead.

Fortunately, we may be able to bring WPF back into the project after the demo. It seems entirely reasonable that a XAML document could lay out Update Controls in addition to the more typical components. This should give us the best of both worlds: parallel development tracks and isolation of user interface components.

One other developer is involved in the project. While Raymond and I are creating the user facing parts of the system, an employee of SSG is working on integration with custom hardware. To mitigate risk on this integration, we designed an interface ahead of time. The interface accepts memento objects instead of business objects to further mitigate the risk of changes to the interface. Whereas business objects are likely to change, mementos tend to be more stable.

The design and techonogy decisions that we've made are driven by not only the requirements of the system, but also the structure of the team that is building it, and the time table in which it must be delivered. In future episodes we'll delve into some of the detail of design and implementation, and let you know how the project is progressing.

16 Bytes

Tuesday, May 8th, 2007

16 bytes seems like a pretty short number. That's only 128 bits. It doesn't take long to write them down. You could even memorize them if you try.

16 bytes is the length of a Globally Unique IDentifier (GUID). Here's one I just generated: "{37B317CC-6036-4037-8E26-0A239103BC2B}". This GUID has never been generated by any computer at any time in history, and will never be generated again.

I can use this arbitrary number to represent anything I want. I can say this is the ID of a new COM interface I design, or a new ActiveX control I author. I can use this as the primary key of a new row I add to a database table. Since it is globally unique, there is no chance of collision if I merge with another database.

There are  3.4 x 10^38 possible 16 byte numbers. They are more rare that they originally seem.

Your social security number is 9 decimal digits. There are only 1 billion of those. It's a much shorter number.

16 bytes can be a trade secret.

How to use email

Thursday, May 3rd, 2007

As a public service, I offer ten rules of email. Apply them and be happy. Ignore them and others will suffer.

How to read email

Consider the source.

Before reading even the subject line, read who the message is from. This helps put the message in context.

Consider your place.

Are you in the To line or the Cc line? Is the message addressed to a large group of people? If you are the only one in the To line, then consider the message important. If you are in the Cc line, you probably don't need to act upon it. If it was sent to a mailing list, then it can probably be ignored.

Read from the bottom up.

Scroll to the bottom of the message to see where the thread began. And even the most recent message can be read backwards. The call to action is usually in the last two sentences.

Move messages out of your inbox.

Once you have acted upon a message, move it to a folder. If you can't act upon it right away, move it to an "open issues" folder.

Organize folders by issue, not by sender.

It's easy to set up rules and folders by sender, but it's not terribly useful. Email threads involving different people will get fractured. It's better to keep emails on a common thread and about a common issue in the same folder.

Create one folder for open issues and another folder for closed issues. Whenever a new issue arises, create a new subfolder for it within open issues. When the issue is resolved, move the folder to closed issues. If you have a multi-stage process, create a folder for each stage. For example, I have five parent folders named "0 Open", "1 Checked In", "2 Test", "3 Production", and "4 Closed".

How to write email

Don't manage through email.

Email is far to easy to misinterpret. If you allow for interpretation, your employees are guaranteed to take it in the worst possible way. If you have a problem with one specific employee, call him to the carpet. If something is important enough to say to all your employees, it should be said in a company meeting where they can ask questions.

Don't reply to all.

Don't be "that guy".

Delete all but the last two sentences.

After reviewing your carefully crafted message, summarize it. Then delete all but the summary. The email should contain only a call to action and enough supporting material for the recipient to follow up.

Give tangents their own thread.

If the subject line or set of recipients no longer fits, start over. Include only the people who care, and pick a subject line that fits. For example, if you receive a request to review a document, don't reply to all with your comments. Instead start a new thread with only the sender, and identify the issue in the subject line.

Call your audience to action.

Always let the recipient know what you expect them to do. Make this the first (and prefferably last) sentence in the message.