Archive for the ‘Cube Life’ Category

Making Support Tools – Slow, but Necessary

Monday, October 8th, 2012

Building Great Code

This afternoon I had to make a lot of tools for supporting the app I've been working on. I'm getting bug reports, and I need to look into the data we're generating, and the only place it really all sits is in the CouchDB. So I had to write some scripts to hit some views and then load up the values I want with some key restrictions. None of it was all that hard, but when I needed to make a change to one of the views on the production database, I had to wait upwards of a couple of hours for Couch to regenerate the view due to the amount of data we have, and the limitations of the EC2 instance we're currently running on.

Soon enough, we'll be on our own hardware in our datacenter, but it's hard to beat the spin-up time of an EC2 box. It's pretty nice. But the performance isn't all that great, and that's what we're really running up against - I/O to the disk and CPUs to process. I'm hoping that as soon as we get the new machines, things like this will be a lot faster.

All told, I got built what I needed, and it's just a matter of time to be able to use it.

[10/9] UPDATE: worked perfectly! Once I got in this morning, I was able to use these views and tools to look into the bug reports and prove that they aren't bugs - the code is working exactly as designed.

Surprising Fact: Cron Jobs aren’t in a Shell

Monday, October 8th, 2012

GeneralDev.jpg

Today I was surprised to learn that cron jobs are not run in a shell. That was really a surprise. The reason it shocked me was that I had the following in a crontab:

  15 05 * * mon-fri  $HOME/bin/startQL | $HOME/bin/campfire -p

where I'd written a nice little bash script to send a piped message to our Campfire room, and I wanted to pipe the output of the startQL command to that room so we'd be able to see what happened without having to check our email.

The script takes stdin and sends it on as a pass-through, but with a copy being send to Campfire. It's a pretty nice little script that just uses the Campfire curl API:

What I found was that the pipe wasn't working because we weren't in a real shell. It was easy enough to fix - just put the commands in a new file and call that, or use a subshell:

  15 05 * * mon-fri  ($HOME/bin/startQL | $HOME/bin/campfire -p)

Learn something new every day!

Really Amazing Working Experience

Saturday, October 6th, 2012

So I've been working at home this weekend, and I have to say, it's an amazing experience. I can take a little time Saturday morning and fix up a new data import table for the business user and not sweat about it Monday morning. Liza and the kids are asleep, and I'm awake, and after a nice bowl of Raisin Bran, I can work for an hour and get some stuff done without having to worry about what I'm missing with the family, or making someone angry that I'm too involved with work.

I can pop open the laptop, write a little code, do a little magic, check it in, do a run, and then call it done. That's pretty sweet!

This is what I'd hoped for when I thought about developing. Something as effortless as just doing the work. Work a little when you have the time, and then during the week, pound out some major hours. But always be flexible enough to take a trip to the grocery store if needed.

When I think of the joys of self-employment, that's it. If I could do that with the place I am now, I'm not sure I'd ever leave. Ever.

It's just that great to me.

Details, Details, Details – It’s all in the Details

Friday, October 5th, 2012

Code Monkeys

Today I was trying to get some work done (from home!) and I got a chat from one of the Data Sci guys I work with in Palo Alto. He was saying that he couldn't find the data I'd just generated into CouchDB. Because Couch is a NoSQL database, we needed to enforce some kind of structure to the documents, and what we chose was the idea of an execution tag. Currently, it's the date and time of the run - to the millisecond, and the name of the division it's running. This is calculated once at the start of the run and then used in all logging and persistence stores so that it's easy to correlate all the data for a given run.

But today, it seems, the execution tag wasn't constant after all. That's a big problem.

So I started to track this down, and I very quickly came to the realization that the massive refactoring that a co-worker had done to try and streamline logging in the application really wasn't preserving the essential nature of the execution tag. It was generating several of them - all a few seconds off.

Now, don't get me started on the need for logging. It's what I think of as essential to serious, commercial, production apps. You just gotta have it. But there seems to be a group of folks that wants to get back to the APL days where there's no logging, no comments, and the strong belief that the code speaks for itself.

I can't possibly disagree more with all these points, but that's another day, as I said. But not respecting a critical thing like the immutability of the primary correlation factor in the data is kind of a big deal. But it's really a detail, isn't it?

The tests all passed - and the code ran, so it's got to be good, right?

Unfortunately, that's far too often the case with a lot of the younger developers I'm meeting these days. Nice guys, and they are capable of writing good code, but I think they just don't want to put forth the effort it takes to pay attention to all the details. After all, there are a lot of them in even the most simplistic of business apps.

I've talked to a lot of younger coders, and they are self-confessed "Lazy coders". Now I know they aren't really lazy, they work hard, but they aren't interested in paying attention to the details, and that's where everything of importance is to be found.

I just wish I could teach them of the need, but I think the only one that can teach them that, is Time. She has a funny way of catching up to even the smartest of folks.

Working from Home Done Right

Friday, October 5th, 2012

trophy.jpg

So today, for the first time while being at The Shop, I did a Work from Home. There have been a lot of times in the last three months that other guys in the group have done it, but today was a first for me. I have to say, the IT guys at The Shop know how to do it right. I've been at a lot of places with VPN access, and it's always the same: you either control a desktop with RDC/VNC or you have full VPN and it's Cisco and locks you out of all other network activity - save that that goes through the VPN.

The Shop does it right - all SSH tunneling and it's all automatic. I open up my MacBook Pro at home and it's exactly the same as being at work. There's no configuration changes, there's nothing to do, or worry about. Email is clean, calendar is there… it's all working just like I was sitting at my desk at work.

Of course, this means working at Starbucks or Panera is also as nice.

Holy Cow! I'd dreamed that this could be possible, but I've never really been at a place that was done this nicely. It's effortless and seamless. You can't ask for anything better than that.

Code Monkeys Don’t Finish Things – Only Start Them

Wednesday, October 3rd, 2012

Code Monkeys

Today I was looking at the check-ins and saw something that was a good idea, but it wasn't really completed. I knew the guy on the team that did it, and I could easily see why he'd stopped half-way through: he didn't see what he'd really started as something more than a little hack to the problem he had. This is one of the things I'd like to be able to teach people, but I'm worried that it's really something that people have to want to see.

Initially, we had one source for merchant data, and one source for demand data. To be fair, we had two: fixtures (static files that make offline testing nice), and a "live" source. We could switch between these with a simple config option:

  # control whether to use the real data sources
  use_fixtures: true

or:

  # control whether to use the real data sources
  use_fixtures: false

But we're moving forward, and it is very reasonable to think that we have multiple sources for each of these. Right now, we have the need to think of multiple sources for the merchant data, while just two for the demand. The coder implementing the need for the multiple sources for merchants added a config option:

  # control whether to use the real data sources
  use_fixtures: false
  # select from 'fixtures' | 'sf' | 'm3'
  merchant_from: 'sf'

to indicate that the merchant data is coming from Salesforce. We can add other options in the code, and this is very nice and extensible.

The problem is that this really doesn't address the logic inconsistency of having those two options in the same file. If we're using Salesforce for the merchant data, what does "using fixtures" mean? Why not just add:

  # select from 'fixtures' | 'sd'
  demand_from: 'fixtures'
  # select from 'fixtures' | 'sf' | 'm3'
  merchant_from: 'sf'

so that we're specifying all the inputs the same way?

This is the finished idea. What was done was only really half the problem solution, and in leaving it half done, it really lead to a lot of confusion that just didn't need to be there. It's this insight that I'd love to be able to teach coders that don't see it.

But given that this coder is way past 20, I'm guessing he doesn't consider himself "junior", and therein lies the other problem: He's not thinking he needs to learn this. Sad, but true.

Anyway… I have to check these changes because of just this level of incompleteness.

Polishing the Seasonality Data

Wednesday, October 3rd, 2012

GottaWonder.jpg

This is something that I find very interesting about people in the business world - their almost complete reliance on Excel. It's as if they can't even function without it. Case in point, JSON data. Here's a section of a file that I sent to a business person today:

  {
    "cleveland": {
      "Academic Tutor": [100.0, 100.0, 100.0],
      "Airplane": [100.0, 100.0, 100.0],
    }
  }

and there are clearly more lines in the file than this, but they all follow this same pattern. I thought it was pretty easy to understand: City, then Service, and then monthly values (12 versus the 3 I show).

But once again, I'm surprised.

The response was "Can you put it into something I can export in Excel?"

Forgetting that they don't know the difference between import and export, I find it really hard to look at the file above and see how it's different from Excel - save the graphs. But you know… that's the tool they know how to use, and that means that's the tool I need to give them data for.

So I modified my code to spit out CSV or JSON.

Worked just fine, but I'm still wondering what the real limits are for a lot of business people.

Added Days to Close for Anticipating Demand

Tuesday, October 2nd, 2012

WebDevel.jpg

In the original code of the seasonality adjustment to the raw demand data we're getting, I had added in a fixed 6 weeks lead time for getting and closing deals. This was given to me by the project manager, and I used it because I didn't know any better number to use. What this allows us to do is to anticipate the demand rise (or fall), and adjust who to call today. Sounds like a good idea, but it could always be better.

This afternoon, it got a lot better. Ryan looked into Teradata at the time (in days) between the first call to a merchant, and the closing of the deal and came up with an average number of days for a deal cycle - bucketed by the category of the business. He sent these to me, and I was able to use this - along with a more general global average number of days for all deals, and use this in the code.

Now we have a solid default deal time, as well as better values for specific categories of businesses. This is great news. It took him less than a few hours, and I implemented it in about 15 mins. Fantastic work.

Ever better. Sweet.

Adding Seasonality Reasons – Against My Judgement

Tuesday, October 2nd, 2012

Bad Idea

After looking at what I had done for the seasonality adjustment of the demand data - something that should be handled upstream of us, but at this time it's not, the project manager decided that he wanted me to add in the reasons to the solution. Something like: Increased demand for Boat Tours in Oct (150%). And then to carry these through to the client so that they can see why the demand is, what it is.

I told him that I thought it was a bad idea because it's not going to last. Specifically, when the demand service is really finished, it's going to be broken out into at least four components:

  • Inventory Replenishment - any decent inventory control system can see what's being sold (rate), the inventory on hand (quantity), and then project the need to acquire more. This has nothing to do with seasonality, but will naturally take care of a lot of demand issues because the more that's bought, the more we'll acquire. Simple.
  • Manual Demand Insertion - there will always be the need for a system to accept manual commands from a wise and thoughtful operator, and in this case, there are folks that will anticipate demand for things that simply can't be put into code.
  • Demand Forecasting - this is where you want to look at the month-by-month sales, see what might be needed - see the lead time for it, see what we have, and then try to plan what's needed based on past experience. As well, it'd be nice to have this part of the system capable of detecting demand much in the same way the manual input is done.
  • Mixing Board - these previous three components need to be "mixed" together with an aggregator/balancer that allows the user to adjust the "signal strength" of each input independently, and then also adjust the output. Very much like a sound board, you need to be able to mix in all this demand carefully and then feed it into the main engine we've developed.

and he knows all these pieces, and knows they are on the way.

So I say: "The final demand system will have seasonality built-in… we won't be able to get at that data because it'll be baked into the operation and data."

"Yeah, but maybe we need introspection on that…" he replies.

"Think about it… it's not going to happen… what if there's a natural 3 month cycle to some service… we'll see it repeated, but we won't know the reason"

"Oh…"

So he presses - despite my objections, and I implement the feature. Then we talk about this afterwards, and he says "Listen, you tell me if you think it's a bad idea, or hard to implement". I about scream. But I keep my cool.

We talk for about 15 mins about this, and I point out to him that I have been saying the exact words he asked me to say to stop this process. He just wasn't listening. And this isn't the first time for him, either. Not by a long shot. It's one of his more annoying features - he just doesn't listen.

It's in there, and it'll be going away, and the users will wonder where it went, or we'll make a complete mess of the system and try to include it in the final demand values. But I think I'm going to say "No" to that. It's just not possible to really do a decent job of that.

Dealing with JRuby Jar Deployments – Reading Files

Tuesday, October 2nd, 2012

JRuby

One of the nice things about using JRuby is being able to use Warbler to package up all the files and scripts into a single jar for easy deployment. One of the problems with that is that some of the common coding statements don't work the way you expect them to - but they do work.

When I had an issue with reading a CSV file in a jar, my solution was to "go to the metal" and work it out in Java. This worked, and it was OK, but it wasn't something that was transportable to a non-JRuby environment, and I wanted to have that. So this morning I tackled just that.

The thing that's been bugging me was that Configulations works just fine in our code - jar or no-jar, so why was that working and the file reads of CSV files not? What I needed to do was to look seriously at how they were being used.

We were using Configulations in the following snippet:

  require 'configurations'
  require 'date'
 
  AppConfig = Configulations.new(File.dirname(__FILE__)+'/../config')

I know that JRuby 1.7.0 doesn't like the use of the '/../' in the path in a jar as it tries to "walk" the structure, so I changed this to a slightly less round-about way:

  require 'configurations'
  require 'date'
 
  AppConfig = Configulations.new(File.dirname(File.dirname(__FILE__))+'/config')

This allows JRuby to handle the parsing of the path as it sees fit and I don't have to worry about moving around within the jar file like I can in a filesystem.

And then it hit me… how we're using the filenames in the reading of the CSV files:

  require 'csv'
  require 'json'
 
  module FileUtility
    DATA_FILE = 'lib/sales/histData.csv'
 
    # ...
    if File.exists?(filename)
      File.open(filename) do |file|
        contents = file.read
      end
    else
      # ...
    end
 
    # ...
  end

and it stood out as clear as can be - the path was wrong. We were looking for these CSV files in a directory relative to the existing directory. How was JRuby to know that we wanted to look within the jar? Impossible.

The Configulations example worked because it used the dirname() method relative to the existing file - which is in the jar. That means that we were the ones telling JRuby to look in the jar (or on the filesystem), and it was all about the path we prepended to the beginning of the file we wanted to load.

There was no need to have the Java solution - we just needed to be more careful with the location of the CSV files. What we have now is far simpler:

  def self.read_file(filename)
    contents = ''
    File.open(project_root + '/' + filename) do |file|
      contents = file.read
    end
    contents
  end
 
  def self.read_csv(filename, key)
    res = {}
    CSV.parse(read_file(filename), :headers => true).each do |rec|
      k = key.map { |c| rec[c] }
      res[k] = rec
    end
    res
  end
 
  def self.read_json(filename)
    JSON.parse(read_file(filename))
  end
 
  def self.project_root
    @root ||= File.dirname(File.dirname(__FILE__))
  end

where once again, we use the "double dir" method chain to get the parent's directory, and use that to know that it's the root of the project - based on the location of this file in the project. This is far, far simpler than we had in the past, and it's removed all the silly Java code that wasn't really necessary in the first place.

It's important to realize you solved a problem, but not in the way it was intended. I'm glad I went back and fixed this. Very glad.