July 12, 2007

Code Complete

Last night, I hit the code complete milestone on the new finance system at work about eighteen hours ahead of schedule. (Code complete means that all features are functioning and working together.)

Now that the system has hit the code complete milestone, it can start being tested at a deeper level by other people and I can start working on making the system look good because right now the finance system looks like an atrocious mishmash of programmer art and ugly dev HTML.

What has changed since the project was started?

We started out using Windows Workflow Foundation to handle our prolonged workflows, but a few things caused us to jettison WF. WF works great in the following two situations:

1. All workflows will complete while the executing process is running.
2. You will never update the workflow DLL.

If you are working with situation #1, WF works wonderfully. It's able to effectively load balance your workflows in such a way that you can have several client tasks going on at once with no real problem. If you are working with situation #2, WF works great as well because with the persistence services, you're set.

However, if you ever have to update your workflow DLL and you are persisting your workflows between launches, you are screwed. Because WF uses binary serialization to persist your workflows, if the version number for your DLL changes or if you digitally sign your WF DLL or if the footprint for your workflow changes, you lose ownership of the workflow on next launch and can't retrieve it even if you create a manifest file that says it's okay to use the new version. That's fine if your workflows are small cheap tasks, but if your workflow takes several months to execute because of needed human interaction, losing that workflow and all of the data associated with it can be fatal to your project and your career. Even if you restore the old version of the DLL to try to get those workflows back, you're still screwed...they're gone. I wish they had tried to use some of the work that the WCF team had done to make it a bit more robust against change, but I guess you can't have everything.

The really sad thing is that the code got significantly simpler after giving WF the boot.

Second, because of conflicting license requirements for currency conversion web services supplied by the various credit card processing houses that we have to deal with, I ended up just using the noon exchange rates web service from the New York Federal Reserve Bank.

If you're using .NET and want to use the service, note that it does return an XML document back as a string. You'll most likely want to use XPath to pull out the appropriate values.

If .NET supported XPath 2.0, it would be pretty simple. You could just use an XPath query like this to get the amount that you'd have to mulitply your U.S. dollar amount by to get the value in the foreign currency:

if (//frbny:Series[@UNIT="USD"]) then (1 div //frbny:OBS_VALUE/decimal(text())) else (//frbny:OBS_VALUE/decimal(text()))

However, .NET only supports XPath 1.0, so you'll have to do it in two steps. First get the value from the XML document:

//frbny:OBS_VALUE

Then check to see if the currency is equal to "USD" and if it is, the value to return is 1/value instead of value.

//frbny:Series/@UNIT

Remember to add the XML namespace to your XPathExpression or the queries will throw exceptions.

XPathExpression expr = nav.Compile(xpath);
XmlNamespaceManager mgr = new XmlNamespaceManager(nav.NameTable);
mgr.AddNamespace("frbny", "http://www.newyorkfed.org/xml/schemas/FX/utility");
expr.SetContext(mgr);

Third, I've grown to really dislike the SqlDataSource control for one reason and one reason only: When you select your connection string from the dropdown, rather than show the name of the connection string, it shows the connection string so you have no idea if the proper connection string is in use unless you memorize the actual connection string.

I've got multiple connection strings in place depending on which server I'm going to be talking to and what the purpose of the communication is. I'd rather see the name of my connection string, like "Oracle.ReadOnly" or "Sql.Finance.ReadWrite" instead of the actual connection string.

I'll try to do a more thorough post-mortem once it's all over. In the meantime, I need to go get some food. I'm on day six without any glipizide and while my morning readings have been a little high, my after-meal readings have been in the 140-160 range and that's all that I can ask.

No comments: