Slight Adjustments

Tonight I set out to fix a drain in the front bathroom.  This was one of the things on my todo list since we moved into our new home over a month ago.  This tub is where we've been giving Hudson his baths and the lever style drain stopper wasn't completely plugging the drain.  Hudson likes his time in the tub, so naturally the water draining out slowly was a problem.

With me being the person that I am, I went to the hardware store and bought one of those push style drains to replace it.  I installed one on the old house, so I knew this wouldn't be an issue.  I never even investigated why the old one leaked, I just knew I was going to tear the old one out and put in a shiny new one.  I removed the two screws holding the overflow plate (the plate with the lever in the middle) and pulled out the linkage and stopper.

I'm about to start removing the drain plate when it hit me: there is a threaded rod on that linkage I just threw in the garbage. That would let me move the stopper up and down and adjust how deep it goes into the pipe.  So, for shits and grins I turned the threaded rod out a few turns and stuck it back in. Amazingly it worked. No more leaky drain.

I could have punched myself. I moved the rod maybe an eighth of an inch to fix the problem. That's it, 1/8 of one inch. I had spent $20 on the replacement. I drove a few miles to the hardware store and burned a little gas. I wasted half an hour going to pick up the parts.  I did all of that to solve a problem that was only an eighth of an inch long.

So many times my solution is to find the biggest hammer and beat until the problem goes away. Tonight I was reminded that sometimes I just need to take a moment to see what I have in front of me. The best solution isn't always to rip something out and replace it. Sometimes a little tweak is all that is needed. 

While tonight I am talking about plumbing, this really applies to life in general.  How much of your life have you tried to replace when all you needed to do was make a slight adjustment?  Even worse, how much of your life has made you unhappy and you've just let it happen without a second thought? Maybe a small tweak is all you need to restore a little bit of your sanity.  Think about it.

Public APIs are Forever

About 5 years ago I was tasked to write a web service that allowed a single client of ours to send us bills and have them returned back with discounts applied in "real-time." At the time I had no idea what I was doing. Really, I was out of school a year at most and I was just doing something that I thought was cool. I never really debated what the API should look like. All I was concerned with was making the damn thing work.

I did manage to get something working, but by then the damage had been done. I created what I consider the worst looking API known to man. I'm exaggerating a bit; however, I really wish I had done some things differently. Today there are several clients using the very same API I had intended to be used by a single entity. Here's what I learned NOT to do.

Don't let your client's nomenclature creep into your API.
Sometimes this is a hard thing to distinguish. Everyone has their own way of describing things. Some of that terminology is industry specific and SHOULD be used in your API naming convention. Anything that's not industry specific jargon should follow your internal terminology.

Letting the original client's idea of a "site" into our API made sense at the time since this was the only client at the time. They have multiple locations from which bills can be submitted. Now that we have multiple clients using this, I'm finding that this element is rather confusing for most new consumers of our API.

Be consistent with how you name everything.
The names of every property, method, and class are the first thing that your client sees. Make sure it makes sense.

I made the mistake of using different terminology between the detail and master record level. In the master I had a field named "totalbilledcharges" that sums the detail field "linecharge". Today I would have omitted the bill level return value entirely. If it was a requirement to have both I would have named the master field "TotalCharges" and the detail level field "Charge".

You can add buckets, but you can't take them away.
The best thing you can do when defining an API is to accept the least amount of information possible to get the job done. I say this because it gives you less opportunity to screw it up before you even need it. If something is ever needed in the future, you can always add it. Once it's there, it's there forever. Taking something away now would be such a daunting task. I would have to approach each client and find out if the item in question was being used and how removing/changing it would impact their processes. People get pissed when something that was working suddenly breaks.

Even something as simple as changing the type might have some serious side effects. Say you had a status field that you defined as an integer. Changing that to a string would break the hell out of your client's code that was trying to convert a new status "ERROR" into an number. It just won't work. That's not to say that you should make everything a string, just make sure you plan ahead.

Everything going forward has to be opt-in.
You can't exactly tell a paying client that they have to change their code to accommodate the new "Super Foo" feature that you are adding to make something work for a different client. The client has other things to do than accommodate your desires to add a field that they don't need or care about. They've made the project work and have moved on to other ventures. Everyone has budgets for money and time that you have to be considerate of. I guarantee no one has budgeted to fix stuff you've broken because you felt like it.

Let's say you are adding a flag to turn off some default behavior. Being aware of default values will help you out here. If I know that a boolean defaults to false, then I can make this opt-out have a negative verb-age. "IgnoreBar" by default will be false which means that we'll use Bar feature unless someone goes out of their way to mark this true. That was the behavior all along and nothing will change for existing clients.

Beware of Bolt-Ons
A danger with being able to add but not take away is incrementallty adding elements which solve an immediate problem but don't take care of the larger issue.

One of the uglier parts of our API is surrounding how the client defines the hierarchy of who the bill is being submitted for. At present there is a "client id", "subclient id", "site" and "parent id" all in use in varying capacities by various clients. Everything but the "client id" was a bolt-on. Had we realized early on that there was a business need to define a large hierarchy, we would have just exposed an array of strings to fill in. You know what they say about hindsight...

I think that about covers all of the things I've managed to screw up along the way. I've learned this all the hard way. Maybe you won't have to do the same. :)

Hudson is a Crawler

This past Friday I stayed home to take Hudson to the doctor and he rewarded me with this show:

I don't think it's possible to overstate just how proud I am of him. Every single day I can see him growing and developing and it's an amazing sight. We're 7 months into this and I still have no clue what the heck I am doing. The only thing I do know is that we're moving forward.

Sieve of Eratosthenes in PL/SQL

Yesterday I posted an Oracle SQL query that would produce primes, but it wasn't an implementation of the Sieve of Eratosthenes. Here for your viewing pleasure discomfort is a version in Oracle PL/SQL.

create or replace package josh.sieve as
   type prime_numbers is table of number;
   function calculate ( the_end number ) return prime_numbers pipelined;
end;
/

create or replace package body josh.sieve as
   function calculate ( the_end number ) return prime_numbers pipelined is
      type bitbucket is table of boolean index by binary_integer;
      primes bitbucket;
      max_sqrt number :=floor(sqrt(the_end));
      i number:=3;
      j number:=1;
    begin
        pipe row(2);

        for i IN 1 .. the_end loop
          primes(i) := true;
        end loop;

        while i <= the_end loop
          if primes(i) then
            if(i<=max_sqrt) then
              j:=i*i;
              while j<=the_end loop
                primes(j):=false;
                j:=j+2*i;
              end loop;
            end if;

            pipe row(i);
          end if;

          i:=i+2;
        end loop;
    end;
end;
/

This is the ugliest one so far. It really burns my eyes!

What I liked
I was kind of pleased to see the pipe row(foo); functionality. This is very similar to yield in c# where I can just lazily return numbers as they are found instead of building up the whole list table in advance.

What I hated
There's a bunch here. First off, I tried to write this with a for loop, but there is no way to define the step value. I had to resort to using a while loop with an external counter variable. It also sucks that I have to define a custom type which is just a table of numbers. Finally, I didn't see a clean way to initialize a table with a size and default value.

Why it burns my eyes
I'm not digging that assignments have to have a colon-equal "foo := 42" operator. To the same tune as basic, I hate that blocks end with an end keyword. This is an especially big WTF when you consider that lines are terminated with semi-colons. The end result is a bunch of colon-ish punctuation littered with end keywords.

Regardless, I was pleased with the performance. Running a query like select count(1) from table(josh.sieve.calculate(1000000)); runs in about 600 milliseconds on our db machine. Not too shabby...

Prime Numbers as a SQL Query

This isn't a Sieve of Eratosthenes algorithm, but it's too good not to share it.

select r
from (
  select rownum+1 r from dual connect by rownum < 10000
)
where
  r=2
  or 0 not in(
    select mod(r,rownum+1) from dual
    connect by rownum<sqrt(r)
  )

This is an Oracle query that will generate all of the primes up to 10,000. The query is not even close to fast, but I think it's kind of funny. It's just your typical everyday brute force method of calculating prime numbers.

« Previous PageNext Page »