Ayende @ Rahien

It's a girl

What I did on 2005

Seems that there is a tendency to summarize all sorts of things at the end of the year, and this is one place that a Blog really help. Here is my end of year summary:

I also posted 822 posts this year, which average to ~70 posts a month, Wow!

Looking back at the last year, so much has happened that I didn't write about in the blog. Part of it is classified, part of it is personal, but it was  good year.

"There is no such thing as an atheist in a foxhole." -- Army Sergant
"Hospital: Where they wake you up to give you a sleeping pill." -- Definate Facts
"Diagnostics are the programs that run when nothing else will." -- Tech Support Slogan
"Money couldnt buy friends, but you get a better class of enemy." -- Spike Milligan

Nasty Hard drive

I think that I trucked down most of my computer issues. I'm looking into the event log, and I can see tons of "This device has a bad cluster" errors.

Experience has thought me that this usually mean that the HD's death is imminent, so now I need to get a new HD, and move Windows (which I just installed on the bad HD, naturally) to the new one. Any suggestions of how to do this? I want to do this exactly once, so I'm not very willing to buy Ghost or friends.

Tags:

Published at

Stories from a Job Interview: Part 1

In a job interview I was asked to pull a list of records from a database and save them to XML. The data was hierarchical in nature, of course. The code I produce looked a bit like this:

 

Dictionary<int, XmlNode> connections = new Dictionary<int, XmlNode>();

XmlDocument xdoc = new XmlDocument();

XmlNode root = xdoc.CreateRootNode("Names");   

       

using(IDbConnection con = GetConnection())

{

      using(IDbCommand command = con.CreateCommand())

      {

            command.CommandText = "select id, parent id, name from Foo order by parent id;"

            IDataReader reader = command.ExecuteReader();

            while(reader.Read())

            {

                  int id = reader.GetInt(0);

            int parent = reader.GetInt(1);

            string name = reader.GetString(2);          

     

            XmlNode node;

            if(parent == 0)

                node = root.CreateChild("Name");

            else

                node = connections[parent].CreateChild("Name");

             

                  node.Text = name;

            node.Attributes.Add("id", id.ToString());

           

            connections.Add(id, node);

            }

      }

}

       

xdoc.Save("results.xml");

 

I believe that the purpose was to check if I know how to use recursion, but it never occurred to me to use it in a case like this. The code above uses a single database query and a single pass on the dictionary. It is, I believe, as efficient as you can get without really trying. The funny thing was that the interviewers tried to get me to use recursion (without saying it), but I literally couldn't understand what they meant until they said it.

Published at

What a developer should know

After reading Joel, and writing my response to that, I started to think about all the things that a typical developer should know in order to produce a working application. The list isn't in any particular order, and I'm focusing it the business / application developer:

  • Data Structures
  • Complexity
  • Cost of operations (local vs. remote)
  • Designing usable classes*
  • SQL
  • XML
  • ACID
  • Basic Design Patterns:
    • Strategy
    • Decorator
  • Regular Expressions
  • Data Modeling
  • KISS
  • TDD
  • Building DAL
  • Change Management (SCCS)

 

 

Anything to add that you think is essential?                    

 

* Reusable classes are another matter all together

 

 

More on repositories and delegates

I like delegates, maybe to the point where I use them too much. I talked about my Repository and that is allowed me to access various sources without caring what the object is, or what is being done with it. Here is a possible implementation of a Finder, which is a read only repository. This code also shows a new pattern that I began to use lately, which is to put delegates in dictionaries, and then invoke them, I find that this is a very powerful way to specialize handling of functionality without much effort.

 

public class Finder
{
    static IDictionary<Type, Func<object, Type, int>> finders;

    static Finder()
    {
        finders = new Dictionary<Type, Func<object, int>>(new IsInstanceOfEqualizer());
        finders.Add(typeof(ActiveRecordBase), delegate(Type type, int id)
        {
            //Get item from the database
            return ActiveRecordBase.FindByPrimaryKey(type, id);
        });

        finders.Add(typeof(Employee), delegate(Type type, int id)
        {
            //Get the user from Active Directory and wrap it in an employee instance
            return new Employee(ActiveDirectoryWrapper.GetUserWithId(id));
        });

        finders.Add(typeof(Enum), delegate(Type type, int id)
        {
            //would be converted to the enum value by Find
            return id;
        });
    }

    public T Find(int id)
    {
        return (T)finders[typeof(T)](typeof(T), id);
    }
}

 

In the case, either the object knows how to save itself (Active Record), or it doesn't need to be saved, so I didn't need to implement a full blown repository (in which case I would've written an interface an each of those would be a class implementing the interface). In my real project, I ended up with the repository-as-a-generic-interface approach, but the one above is a very real possibility for all those that use a real Active Record model (I needed more, so I modified it quite a bit).

Why is this good for? Well, consider other generic code, which can use this Finder object without needing to know what it does (think about security, logging, simple UI, etC).

 

Published at

My Thoughts about Joel On Java

It seems that Joel's article about Java in the Universities has made quite a bit of noise. I think that I will add my two cents to the mix.

Two things pissed me about the article. The first was the claim that CS should weed the bad from the good, and the second was his treatment for OOP (agonizing over has-a & is-a is so not OO).

I completely disagree with the claim that CS* classes should function as discriminators between good and mediocre programmers. That is his problem, as a potential employer, to weed out the great from the mediocre. And if he can't do that in about a week, regardless of technology or project that they use, then the fault is with him, not with the graduate. Being proud because you were up until 4:00 AM chasing a null pointer dereference and broken stack is not the sign of a productive developer. I wrote all the linked lists and hash tables that I ever intend to write. Hell, I even spent some time writing a sparse matrix in C++ that was multi dimensional and efficient. I see zero need in them outside of the class room.

I will walk out of a job interview where the test is being able to write a linked list in C or C++. I have done so before where the questions where at about this level. You want me to deal with pointers, give me a job that requires that I will deal with them. That has better not be a bug tracking software in ASP 3.0 (what Joel sells).

The second part, about OO design being "spending countless hours rewriting your code to rejiggle your object hierarchy, or you fret about faux 'problems' like has-a vs. is-a" is just bull. I'm programming since I was ~14 years old or so (if you don't count making a turtle dance in Logo), I started in Pascal & VB 3.0, I later moved to C & C++, and then to C#. The last time I thought about has-a vs. is-a was when I was learning C++ and was very new to OO. Since then, I didn't touch this stuff, and I produce good OO code. Most of it is online, and you can check it out.

Then there is the whole premise of the article, which seems to be that you need to find a Segmentation fault [sic] and fix it in order to be Real Programmer. Been there, done that, boring. I consider good design much better than knowing how to handle naked pointers. You know what, show me a good C++ coder that still go on to do that, when all the best practices for C++ are full of RAII and safe pointer wrappers.

 

Now, that said, he does have some points that I agree with. Recursion is a good, clean, way to solve a certain set of problems, and understanding pointers is very helpful in understanding how computers think. And understanding functional programming is very good for being able to write clean code later on.

 

* I'm currently studying for a CS degree (just started), but I know about 85% of what the degree will teach me because I wanted to learn and a university wasn't a possibility at the time. I've a job, which I like, and I got it because I like what I'm doing, not because I had a degree or a certification.

Published at

Lot more SQL Fun

While I need to read some ugly code, I think that I'm learning SQL in leaps and bounds. Right now I gave a shot to the OUTPUT clause in 2005, and it is just great.

Published at

All Hail The Unit Test

Just listen to this song, it may be not the greatest musical in the history of mankind, but it is very funny nonetheless.

Published at

What I want to see next...

Brad Abrams talks about a new method in the CLR [ ReadAllLines()  ], and the comments list several useful stuff and some wishes.

My wish for the new year is to be able to declaratively set a dictionary. What do I mean by that?

int[] values = { 1, 2, 3, 4, 5, 6 };

But just try to do that for dictionaries:

Dictionary<string, int> values = CreateDictionary();

 

private static Dictionary<string, int> CreateDictionary()

{

    Dictionary<string, int> values = new Dictionary<string, int>();

    values.Add("one", 1);

    values.Add("two", 2);

    return values;

}

 

And this is the best thing that I could come up with! Other languages has initializers for dictionaries. This is something that I use constantly (very easy to setup a relationship between behaviors, for instance), and it's annoying me every time.

Published at

Silly Answers, Silly Buggers

I just saw this, and I nearly ruined a perfectly good keyboard. Those answers are so… stupid. But I fully believe that actual people answered them in response to actual questions in interviews. I distinctly remember walking out of an interview knowing that I'm not willing to work for those guys, based on the questions that they asked me. (My philosophy is that I want the interview to be hard, I want to make sure that I work with people who know more than I do, and I want to be sure that I'll need effort to keep up with them.)

In the spirit of Groucho Marx: "I wouldn't want to belong to any club that would accept me as a member.", I wouldn't want to work in a place where the work is too easy or the standards are too lax. What is the point, then?

 

Published at

Movie Review: The Lion, The Witch and the Wardrobe

This is, without a single doubt, the best movie rendition that I've ever seen for any book. I'm talking about near 1:1 similarities between them. After being disappointed by the cut scenes in Harry Potter #4, this movie just made my day. I recognized everything, and I couldn't find a missing piece in the story. Everything was there, and the changes that were made were very small and reasonable (a living fly instead of a living one, Lucy falling asleep, Edward betraying the goat-man (sorry, I've no idea how he is spelled in English), etc).

Excellent!

The battle scene is simply wonderful, at the same level or better than any of the LotR scenes.

Published at

Best quote in the MSFT vs. GOOG fight

I got to hand it to Scoble, this quote is the best one I've seen so far in the Microsoft vs. Google fight:

Do you count your 20% time out of an ordinary 40-hour-workweek? Or out of your 80-hour superweek?

Published at

It's hard to stop me from talking...

Or so I have been told.

So, while I'm installing all the highly essential applications and configure Windows properly (running Windows Update now, which takes a while), I might as well spend some time talking about other stuff. My computer is down since Sunday, but only now I could get the time to do something about it. So, I'm having a wonderful time catching up on my feeds, I've ~400 messages to read, which is a bummer. I managed to keep up with my personal email, but not with the stuff that reached Gmail, so I'll have to look into that later. Oh, and 50 of those posts are Scoble's. Spooky.

Can someone please tell me why I have to reboot in order to install Windows Update?

I'll probably not finish it today, I now downloads the service pack, which would take forever and then a little bit more. That would teach me to install from an original CD.

 

Published at

Something to fill the silence

So, I've been quite in the last couple of days since I work where I don't have internet connection that I can easily use, and my home computer finally decided to kiss the pavement and dropped dead.

I'm pretty sure that the problem is that I used a SATA HD for this, since most of the problems with it started then. Seems to me that my BIOS is a bit freakish about SATA HD as a boot device, and the Windows installer needed a bloody floppy drive before it would recognize it.

I tried to fix it using the recovery console, but I couldn't fix it in five minutes, which mean that I could probably fix it in five hours, but that would take a lot more effort than I need. I'm currently installing Windows on an IDE drive, so hopefully I'll be able to restore most of my stuff quickly. Of the three applications that I use the most, Outlook & RSS Bandit can easily be set to their previous state, and my VS.Net installation isn't customized to any significant degree.

My sole worry is that I would have a hard time porting my SpamBayes settings, but that should be easy enough, considering that it is just flat files.

Published at

SqlClr and anonymous delegates

Okay, I'm playing around with SqlClr (doing runtime DDL stuff, mainly), and I discovered that SqlClr and anonymous delegates don't play along very well. The issue is that code like this:

CallWithAction(delegate(DateTime dt) { return dt; } );

Is actually translated to something like this:

if(ClassName.<> HiddenFieldName == null) 
            ClassName.<> HiddenFieldName = new SomeDelegate(HiddenMethodName);
CallWithAction(ClassName.<> HiddenFieldName);

And that causes problem with the SqlClr, since the method is trying to store into a static field, which doesn't seem to be allowed on the safe level (which I really don't want to pass.)

It doesn't happen if the anonymous method uses local variables, since then the compiler generate a completely new class. Just a little gotcha that I would've never solved without Reflector.

Tags:

Published at

Reflection & Locale

Seems like I made a mistake in the previous post. The problem with the Turkish I wasn't supposed to be solved on the Reflection API level, but earlier than that. The issue seem to be the string transformation done on a property name (for instance, if I want NHibernate to use the field and not the name) rahter than what I pass to GetField() and deriatives) where NHibernate didn't took into account that lowering the case of a character may result in a different letter on other locales. The issue was already fixed on NHibernate CVS, and I'm feeling stupid for crying wolf.

GetField & Turkish Locale

I've just got an email about an interesting problem with NHibernate. After searching for a little while, I came up with the conclution that the problem is in the framework, and I can't see an easy solution for it.

First, let's start by explaining Turkish locale & its unique interpretation of the humble i letter. This MSDN article does it best, so I'll let them do the job:

For nearly all Latin alphabets, including U.S. English, the character i (\u0069) is the lowercase version of the character I (\u0049). This casing rule quickly becomes the default for someone programming in such a culture. However, in Turkish ("tr-TR"), there exists a capital "i with a dot,"  character (\u0130), which is the capital version of i. Similarly, in Turkish, there is a lowercase "i without a dot," or (\u0131), which capitalizes to I. This behavior occurs in the Azeri culture ("az") as well.

How does it has anything to do with NHibernate? Well, consider this code:

typeof(Blog).GetField("id")

This code will fail to return anything if the locale is set to Turkish. And the problem is worse because there doesn't seem to be a way to pass an InvariantCulture or Ordinal to the GetField method. The only solution that I can see now if to get all the fields on the method and iterate over them one by one, making a culture insensitive comparision.

Please tell me that I'm missing something, since this seems to indicate that any Reflection code will likely break if you're using the Turkish locale.

Oh, and that is the behavior for both .Net 1.0 & .Net 2.0

Picking a database... and staying with it.

I just read this post from an ex-googler, talking about how they tried to move from MySQL to another (presumbely Enterprise. Most likely DB2 or Oracle), and failed. I'll gloss over the fact that they implement such a critical system over a database with no trasaction support (and the comments about "it is easy to add on the application level" that made me cringe so hard that for a moment I looked like a fat bagel).

I want to talk about migrating a single application from one database to another. This is not something that you see done often when you're talking about Line of Business application. Especially not in-house one. But, and this is important, it's both possible and straight-forward in most cases if (and only if) you know something about layering and seperated the database from the rest of your application properly.

If my recent ActiveRecord & NHibernate would ever need to support anything beyond SQL Server, here are the list of steps that I would need to do:

  • Create an equivalent schema for the other database (most probably automatically via some tool).
  • Research on how the connection string looks like and put it in the config file.
  • Re-write a single trigger, which is done in T-SQL for efficently.
  • Run the tests against the new database.

Total time that this should take is two days, and I'm including in that the time that it will take me to install the database (even if it's an Oracle) and then learn its SQL deriative.

Now, that is not to say that I might get into places where the application relies on some SQL spesific behavior (identity columns, for instance), or something similar, but those should be isolated incidents. The application itself should move without anything major happening. You know, if I didn't have NHibernate, I would have had to re-write all the queries from scratch, and that might have added a week or so if I had really complex queries (I honestly don't know, NHibernate handles it all). But the application would still not know that anything had happened. The one caveat that may happen is if I needed to port it to something like MySQL 4.0, which doesn't support Transactions, Nested Queries, etc.

This is ABC in application design. The inability to move from one database to the other means that your application relies on way too much on database spesific stuff. There may be reasons for this, but I think that saying something like: "You get what you pay for, and with free you get much more..." is bullshit.

NHibernate Generics & EntityList - Update

Okay, the code is in the Subversion repository, but I'm not ready to release it yet. I've a shameful confession to make first:

I never used an IList with NHibernate (or ActiveRecord) before, so I'm not really sure whatever my code makes good sense or not. And, in addition to that, I haven't had the time to test it propertly. I would appriciate it if someone who had used it before could take it for a spin and tell me what they think the bad points are.

ReSharper & VS.Net: Kaboom

I'm running ReSharper #213, and it's aweful. Exceptions are all over the place. The problem is that it is so feature packed that I have a hard time living without it. I just had an instance where doing a refactoring on R# caused the entire IDE to vanish.

It's the hardware fault, honest!

As a software developer, I tend to assume that any excuse that blame the harder is an excuse. But I just got two stories that are completely related to hardware to disprove myself.

A few weeks ago I shoved another 1GB RAM into my computer, and all was well, until I started to get random application failures and a BSOD from Windows. Completely random, completely unexpected. And that is after running seemlessly for over three years without really noticing that th OS existed. Turned out that I needed to play with the positioning of the RAM sticks on my board, and now it seems to be working.

The other thing just happened, I was having bad connectivity issues since just about forever, and today the technician found what the problem was, the connecting wires led to a concret bore, which was full of water... You can imagine what happened next.

Song search

This post is for the readers from Israel, I just listened to a great song in the radio, but I didn't catch its name, and I can only remember two lines:

את כותבת שורות קצרות,
אני כותב לך מגילות

I heard it on Galgalatz, and I would like the name of the song and the singers.

Tags:

Published at

String Comparison Performance

During the Tel Aviv Launch I had an argument with Justin about the performance of string comparisons. The issue was whatever it is good to use str == "" or str.Length == 0, and the meriths of string.IsNullOrEmpty(str) in .Net 2.0.

Since Stefano just posted a comparison in VB.Net, I thought I would make the same check on C# (since VB.Net uses a compatability layer for string comparison). I merely ported Stefano's code:

DateTime start = DateTime.Now;
string str = "";
for (int i = 0; i < 1000000000; i++)
{
   if(str == "")
   // if (str.Length == 0)
   // if (str == string.Empty)
   // if (string.IsNullOrEmpty(str))
   {
      ;
   }
}
TimeSpan elapsed = DateTime.Now - start;
Console.WriteLine(elapsed.TotalMilliseconds);

The results?

str == "": 11671.875
str.Length == 0: 3984.375
str == string.Empty: 22265.625
string.IsNullOrEmpty(str): 9703.125

I'm not sure what is the reason for the big different between comparing to "" and comparing the string.Empty. An interesting experiment that I did gave the following result:

object

.ReferenceEquals("", str) is true

object

.ReferenceEquals(string.Empty, str) is false

No idea what this means, though.

From my point of view, if a billion iterations are needed to even see the changes, it doesn't matter which you choose anyway. Just to give you a hint, a call to str=="" will cost you 0.00001 milli-seconds. You'll need to find a physicist to tell you how to call this amount of time, and even then, he will look at you strangely ever after.

Tags:

Published at

More on .Old

While discussing the afore-mentioned "system" of keeping track of changes with a friend, she mentioned (she used to deal with intrusion detection for web apps) that she broke several system by systematically trying "Page.aspx.old", "Page.aspx.tmp", "Page2.aspx", etc for all the pages in the application. The booty was usually the page source, and in most cases, that came along with the full (unencrypted, naturally) connection string, including the "sa" password.

What was your experiance?

.Old, .Tmp, .Bak - The Source Control From Hell

Imagine the following file listing:

do_stuff.old         do_stuff.tmp                  do_stff.sh1
do_stuff.bak        do_stuff.sh.jhon              so_dtuff.sh
do_stuff.sh.2       do_stuff.sh                    tmp_dostuff.sh
do_stuff2.sh        do_stuff2.new                
do_stuff3.old       do_stuff2.newer
do_stuff2.sql       do_stuffie.sql.old
do_stuff.sql         do_stuff.sql.dont-run

Quick, which file is the correct one? Now multiply that by ten, then add at least three different types of scripts, a truly random directory structure, file names that were written by someone with only half a keyboard and that was taxed per letter, and you may see what I did today. Oh, and just for kicks, spread the responsibilities randomly between shell scripts, sql scripts and stored procedures.

Did I mention a case senstivie file system yet?

Tags:

Published at