Tuesday, June 27, 2017

Working on a tool for a hackathon

We have a culture of regular time devoted to hackathons.  We can work on what we know is important or fun or challenging - we have free reign to take on the projects that we are motivated to complete.

For my project, I am working on classifying some of our test code.  What I have to do specifically is parse through each test file looking for attributes in the code.  The goal here is to make a tool that does this so I never have to do it again.

I've been working on this for a day now, and I am reminded why I want this tool.  I have written TONS of code that opens a text (type) file and goes through it line by line.  It is always tedious, slow and I always have to look up how to do these basic tasks on the internet.  Since I only do this once every year or so, I forget the exact syntax to use and need a continual refresher.

But I got my tool done yesterday and am making sure it works today.  Then I want to move it from a command line to a nice visualization that I can monitor for changes…

Questions, comments, criticisms and complaints always welcome,

Friday, June 16, 2017

Test names that make sense

One of the tasks developers have is adding a test when making code change.  That is just good engineering practice - you always want to make sure your code works, and then when I make a change, I want to test that I did not break your code.  It's pretty self-explanatory, really.

The trick here is that when someone fixes a bug report.  Bug reports are tracked by number, so I may be working on bug 1234 today.  When I get a fix in place, I need to add a test.  Now, when I add the test, I need to give the test a name.

One easy way to name the test is naming it after the bug number being fixed, like this:

That makes it possible for anyone else that needs to look at this code to know to check the bug database for details around bug 1234.  I chose the word "possible" there specifically because while it is possible to do this, it is time consuming.  I have to switch from my IDE (I use Visual Studio) to the bug tool and dig up the bug report. 

Now imagine if I had named that test this instead:

Now if I am reading that code, or investigating a failure, I have a MUCH better starting point.  I know that the test I potentially broke had to with French regions and maps.  If I am changing map code, I am very interested in what I might have broken and know where to start my investigation much more quickly.  I don't have to switch out of my IDE to get this data and it saves me a little bit of time overall.

So while I am going through tests, I am not renaming the old format with a bit of descriptive text.  The next challenge I might take on is trying to quantify how much time I am saving overall.

Questions, comments, concerns and criticisms always welcome,

Tuesday, June 6, 2017

Hardware at Tableau

I just noticed that I was working in a remote office today and logged into my primary desktop from that office.  I also realized I never documented the great hardware we use at Tableau.

It may not seem special, but all the developers here get at least 2 machines: one Windows and one Mac.  We need this since our 2 primary desktop clients both need coverage.

I chose a Windows desktop and that is what I use for email and such as well as writing code for Tableau.  It's a state of the art 16 core (or 4 depending on how you count hyperthreads) 32GB desktop.  I also have 2 monitors on my desk - a 24" HD monitor and a 22" 4K monitor.  I have learned to rely on multiple monitors since way back in 1998 and can't imagine working with only one.  Brrrr.

Since I run Windows 10 on my desktop, I got a Mac laptop for portable usage.  Nothing special here - 16GB Ram and whatever processor they were using last year (I have never checked).  I use it for note taking in meetings and general office type usage.  If I need to write code or debug or whatever, I will remote into my desktop.

And finally, the docking station I have in the remote office is even better.  It has 2 monitors and I can use the laptop as a third monitor.  In effect, I get a three monitor setup when I work remotely and that is tremendously handy.  I put Tableau on one monitor, my debugger/Visual Studio/Pycharm on the second and email/chat clients/reference notes/OneNote on the third.  It really speeds me up and is a nice perk when I can't get into my main office.

Questions, comments, concerns and criticisms always welcome,

Thursday, June 1, 2017

An upcoming side project for the test team

We voted this week to dedicate an upcoming sprint to focus on becoming more efficient as a team rather than focus on any given new functionality for Tableau.  The thinking here is that if we become 10% more efficient, we can deliver 10% more features in a given release over time, so this small investment now will pay large dividends in the future.

The test team chose to work on analyzing automation results.  For instance, if a given test is known to fail some large percentage of the time - let's say 99.99% for sake of argument - then if it fails tonight I might not need to make investigating it the highest priority task on my plate tomorrow.  Similarly, a test that has never failed and fails tonight might very well become my most important task tomorrow.

So what we are doing in our first steps is determining the failure rate of every single test we have.  Just tying together all that data - years worth, times several thousand tests, times multiple runs per day, et… - is a large challenge.  Then we have to mine the data for the reason for each failure.  If the failure was due to a product bug, then we need to factor out that failure from computing how often each test intermittently failed.

The data mining and computation for all of this seems like a good, achievable goal for one sprint.  Using that data in a meaningful way will be the (obvious) follow on project.

Wish us luck!

Questions, comments, concerns and criticisms always welcome,

Tuesday, May 23, 2017

Sharing lessons from moving test code around

I mentioned 2 weeks ago that I was moving some tests around within our codebase.  That work is still happening and will almost certainly continue for quite some time.

One other task I am taking simultaneously is quantifying the cost of moving these tests.  This ranges from a simple hourly track of my time to including the time others need to review the code and validating the tests achieve the same coverage once they have been moved.

I'm also taking a stab at quantifying how difficult moving a test can potentially be.  For instance, a traditional unit test that happens to be in a less than ideal location is a good candidate for almost a pure "copy and paste" type of move.  Since the test is sharply focused and doesn't have many dependencies, it is very simple to move around.

Other tests that start with loading a workbook in order to validate a column is being drawn correctly (I am making up an example) have many dependencies that have to be untangled before the test can be moved.  This is at best a medium difficulty task and can easily take a large amount of time depending on how tightly both the test and product code are woven together.

For now, I am making notes on how to untie those knots and moving the tests that are easy to move.  Once I am done with my notes, I intend to look through them for common patterns, good starting points and use that data to develop a plan to start untangling the next round of tests.   And of course I will share this with others since I doubt I will have enough time - or energy :) - to do all this myself.

Questions, comments, concerns and criticisms always welcome,

Monday, May 15, 2017

All Hands Week

This is a bit of an unusual week.  We have booked the Washington State Convention Center in downtown Seattle for our annual company meeting.  "All Hands" is the navy phrase that we use to show that the entire company attends - we go over business strategy, technical planning, development specific tasking, TableauConference planning and so on.

I can't write much about any of this (maybe not so) obviously.  This will be my second such event and I learned a lot of information last year.  Now that I know where to focus, I expect this year to be even better!

Otherwise, I am still moving unit tests to better locations.  The easy tests to move will likely fill this week for me and then next week the work gets more challenging.  Stay tuned!

Questions, comments, concerns and criticisms always welcome,

Wednesday, May 10, 2017

Moving unit tests to better locations

Last week I spent identifying and removing dead code.  For what it is worth, the biggest challenge there is proving the code is not actually used.  If you know of a way to tell if an operator overload is actually called, let me know…

This week I am focused on moving some of our unit tests to a more proper location.  Some of our older tests are part of a large module that runs tests all across the product.  For instance, suppose I want to test Kmeans clustering.  As it stands right now, I either have to work some command line magic to get just those tests to run, or I run that entire module which tests areas in which I am not interested (like importing from Excel).

A better place for the Kmeans test would be in the same module that holds the Kmeans code.  That way, when I run the tests, I focus only on testing the code in which I am interested and don't need to worry about Excel importing.  There are also some speed benefits when building the test code.  Right now, the old project has references all over the product.  It has to have those wide ranging references since it has such a wide variety of tests in it.  That means a lot of file copying and such during compile time.

One of the other benefits I expect to see when done is that the time to build that older test file will shrink because I am removing code from it.  As I move the code to the correct module, I am updating all the references it used to minimize the amount of references needed to build.  So my module build time will go up, but not as much as the time saved from the older test pass.

There is one final benefit to all of this.  When I build my module now, I build both that module and the older test code.  This is necessary since I need the testing provided there in order to test any changes being made in my module.  Once I am done with this task, I will only need to build my module in order to test it since all the tests will be part of the module.  I will no longer have to "pay the price" of  building that older test project.

Questions, comments, concerns and criticisms always welcome,

Monday, May 1, 2017

Tabpy on a Pi Tablet !

I built a Raspberry Pi powered tablet last week and brought it in to work.  Naturally, I couldn't resist the near alliteration of "tabpy on a pi-tab" so I pip installed tabpy:

Running tabpy on a Raspberry Pi Tablet

A Pi is pretty low powered so it won't run fast, but should be fun to play with.

Questions, comments, criticisms and complaints always welcome,

Wednesday, April 26, 2017

Removing Dead Code

Last week I was working on code coverage.  One of the results I saw is that some of the source code we own is not used by any of our testing - it is 0% covered.  Digging into this, I found out that this code is not used at all by Tableau so I intend to start removing it from our codebase.

Code that is not used by the product you are working on is often referred to as  "dead code" and there are a few ways this can happen.  One obvious way is that existing code functionality simply gets provided by something else.  Let's say you had a very slow Bubble Sort routine to sort a list.  Once you learn a faster algorithm to sort, like Quick Sort, you start using the Quick Sort instead.  If you are not diligent when making the code change to use Quick Sort, the Bubble Sort code can get left behind.  It is not used at this point and becomes "dead code."

Similarly, if you need to implement sorting, you could try Quick Sort, Insertion Sort and Merge Sort (for instance).  Once you profile the time spent by each routine and generate the test data, you can make a decision about which routine to use.  Again, if you don't remove the routines you don't use, they become "dead code." 

After digging into the code coverage numbers, I found a few instances of the second case.  Since this code is not used at all, it doesn't get compiled so that helps mitigate having it around.  But it still results in a good amount of unneeded code stored on everyone's hard drive, maintained in our repository and so on.  The best practice is to just get rid of it and that is what I am working on now.

Questions, comments, concerns and criticisms always welcome,

Wednesday, April 19, 2017

Working on a code coverage task this week

For this week I am focused on getting code coverage numbers for our team.

Challenge #1 is pretty simple to state - get a list of all the source files that our team owns.  And while the problem is easy to understand, the real world implications of this are a bit trickier, especially with older files we have.  As the years have gone by, ownership of unchanging source files gets a little fuzzy.  The team (or developer) who created the original source file may be long gone.  Even teams that own a file might have been reorganized - several times over - since the file was checked in. 

So if Carol created the original "stats.cpp" file she may be the only person to ever have edited it.  If she moves to another team, and her old team gets reorganized, ownership can get moved to the bottom of the list of problems to address at that point.  After all, if the code is stable, why spend resources on tracking who should be associated with it? 

But after a while, every company has this challenge.  That is what I am sorting out this week.

Fortunately, Tableau has been pretty good with naming conventions for source files.  For example, all Cluster related files have the text "cluster" in them.  For most of the features my team owns, I can simply search by file name to get a good starting point for the files we own.  Getting that list together, parsed and cleaned up is my goal for the week.

After that, I think I may need to move to class based ownership.  More on that next time.

Questions, comments, concerns and criticisms always welcome,

Tuesday, April 11, 2017

A nifty site to see what a compiler does to C++ code

While investigating a very intermittent unit test failure this week, I noticed an anomaly in our code.  This test is written in c++ and had an extra semicolon in it:

We had a test that did something like this:
Worksheet wb = wbc->GetSheet();
 CPPUNIT_ASSERT(blah blah);

Notice that extra ; in the middle?  Since the test intermittently fails, I was looking for anything unexpected in the code.  This is unexpected, but I also needed to know if it was important.

Matt Godbolt created a terrific site that lets you put in C++ code and see what output various compilers produce.  The site is here  https://gcc.godbolt.org/

You can choose different compilers and I just took a look at gcc 6.3 to see if it would ignore an extra ;. 

Here's my test code:
void test()
int x=1;

And here is the output:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 1
        pop     rbp

I get the same output with or without the extra semicolon. This is great since I would expect the compiler to get rid of blank commands like this.  Since the compiler does indeed ignore this typo in the code, I can move on to other avenues of investigation.

Give this site a whirl.  You can choose several different compilers and chip options, pass parameters in and so on.  Thanks Matt!

Questions, comments, concerns and criticisms always welcome,

Friday, April 7, 2017

Always learning

One of the most fun aspects (to me) of being in the world of software development is the focus on continual learning.  The systems on which we work are constantly evolving.

For instance, when I started way back in 1995, Windows 3.1 was the dominant OS.  Smart phones did not exist, tablets were only on Star Trek and so on.  The challenge is how to adapt to a constantly changing world.

The strategy I use is one of constant learning.  MOOCs have made this much easier in the last few years - I tend to prefer a combination of lectures and hands on to learn a new skill.  To me, which new skill to learn is not as important as always learning something.  Sometimes there is a skill I need to learn that is very relevant to some project that is upcoming - for instance, learning Python because our automation system here at Tableau uses Python is an obvious example.

But other times I take on tasks that are more in a hobby space.  To this end, for the past few months I have been enrolled in a Programming the Internet of Things program at Coursera.  It has been a blast!  It made me brush up on electronics I learned years ago in the navy as well as some programming in C and Python.  I got to work with the Raspberry Pi and Arduino, install some useful libraries and my final capstone was a device that would email me if I left my garage door in my house was left open.  This was a beginner level class so the introduction was very gentle and definitely left me wanting more.

I received my final certificate this last week and am thinking about going deeper into another class.  Either that or finish the growing list of project ideas I developed while taking this class.  Either way, it should be fun!

Questions, comments, concerns and criticisms always welcome,

Thursday, March 30, 2017

Some simple data munging work this week

A task I had to deal with this week was this one.  I was given a data set we want to test, like this:
2 3 4 4 5 5 6 6 7 9
We have an algorithm to compute some basic statistics on any given data set (mean, median, variance, etc…).  Nothing special about that.  And I had two data sets - the small one above, used mostly to make sure the testing code would work, and another data set of 50,000+ floating point numbers:
-8322.458 -6199.002 -6002.999 and so on.

What I needed to do was compare the results of those types of calculations across a variety of different tools which also compute those basic stats.  I chose Excel, R, some Python code I wrote myself, Numpy/Scipy and Octave.

And that is where the problems came in.

My original data sets were simply a list of numbers, without commas, seperated by spaces, all on one row.  For the small data set, for all the tools, I could just copy/paste or even retype to get the data into the format the tool wanted.  This is not a hard problem to solve, just tedious.  The industry calls this "data munging" (getting the data from the format you have into the format your tools needs) and is almost always the most time consuming part of any analysis.  Hit me up for links to prove this if you want.

For instance, excel prefers a single column to make entering the calculations easy, but can use a row.  Python's read csv files wants commas to separate values along a row (you can specify spaces) but once the data is imported, it is easiest to have one column of data.  So I had to create a file of the 50,000+ values with each value on one line.

R was able to use the same file as Python.  Nice!

Octave  wanted all the values on one row so I had to re-layout the numbers with a comma in between each.  Since this was a one-off task, I simply used Word to edit the file.  It took a little under a minute to make the 50,000+ replacements.

Now I have the data files in the format that all the tools want, and can use their results to help ensure Tableau is getting expected answers for these basic statistics.

Questions, comments, concerns and criticisms always welcome,

Monday, March 27, 2017

Getting our automation to 100%

One of the challenges we have taken on since last year has been driving our test passes to be 100% automated.  Historically, a test pass has had a manual testing component - a human sits in front of a computer that is running a build of Tableau that we intend to ship to customers and validates the responses to test cases are the results we expect.  This can be a challenge because, as often happens, the manual testing phase can take more than a day.  If we get a new build of Tableau each day - and we can get them more often than that - it is possible that we can never finish a manual test pass. 

It also can become very difficult to predict when a manual test pass will conclude.  If there are changes, test will need to validate if the change was expected or not, and while that may be obvious in some cases, it is not always the case.  Imagine that a new piece of hardware, such as a very high resolution monitor, comes out the day before the test pass.  It could be the case that no one on the team knew this would happen and no one has planned for how Tableau should work in that case.  This can slow down the evaluation of the results of the test pass.

To get around this, we embarked on getting enough automation in place that we can avoid all manual testing when a new build of Tableau is released for testing.  For us, this just means looking at automation results and verifying that no tests are failing.  That is my definition of 100% - that we can make our decision based on automation results.  That was a lot of work to get here, and even more work to remain at this level, but it does make testing much more reliable and predictable as time goes by. 

This also speeds up the new monitor test example I gave above.  At this point, all the test results are available so the extent of the test pass is done "all at once."  There is no  back and forth with trying each feature one at a time on the new monitor - all the automation runs and gives a report to the humans that can now start making decisions.  Without the automation, the decision process is slowed down by data gathering, and this is where automation can really speed up the cycle.

We did some tasks other than simply adding automation to help us get here and I'll cover those next.

Questions, comments, concerns and criticisms always welcome,

Friday, March 24, 2017

Some reading for my team

We are gathering books today that folks on the team have and use.  I made my contribution to the first pile of books:
The bottom most book - Doing Bayesian Data Analysis - is my contribution to the pile.  We actually had a learning group here last summer using that as our text and I really liked the way it was written.  Very useful, tutorial and plenty of examples. 

I also have a copy of the top book - Causality by Judea Pearl.  To me, that is the key attribute of what people want from statistics : did changing THIS cause THAT to happen?  I'm still working my way through it and am probably about 20% done so far.  Much more dense than I expected, but that is just another way of saying it is filled with information.

Books are a great way of continually learning and I will write more about that next.

Questions, comments, concerns and criticisms always welcome,

Tuesday, March 14, 2017

Using Virtual Machines to help me test

Tableau gives me a couple of ways to maintain virtual machines for a test environment.  My preferred method is probably seen as "old school" at this point.  I create machines locally to run on my nice desktop machine and use them in Hyper V.  This gives me the most control of my machines, they are always accessible and I can maintain them when and how I prefer to do so.  Right now, since I am still setting them up, I have a 32 bit Windows 7 machine, a 64 bit Windows 7 machine and a Debian machine.

We also use Openstack more globally and I would be willing to bet most folks around Tableau use it by default.  It has its own advantages - I can just select what OS and such I want, and the server builds a machine for me. I can log in when it is done, it doesn't consume any resources from my desktop and I can otherwise use it as needed.  I have not had any problems with this system at all: I simply prefer to run my machines locally since that is the first way I was introduced to using VMs for testing and development.

Informally, I once did a little experiment to judge how much faster I could be as a tester with more machines.  At one point, when I was testing email clients and servers, I had 14 total machines set up in my office running many different languages of clients and servers to validate email sent and received in these situations.  I was able to go about 50% faster by having all these machines under my control than by relying on labs or others to set up and maintain them.  I could also make any changes I wanted whenever I wanted since I was the only user.  The downside was learning to be a client and server and network administrator since I had to do all the work myself.  That probably paid for itself in the long run - the better I know my product overall the better my testing will be - but I did not factor that into my calculations of spending about 1-2 hours per day tweaking all the machines.  One last advantage with this many machines is that while updating them took quite a bit of time, since I updated them one after the other, one machine was almost always ready for testing while the rest were being upgraded.

And now, back to work!

Questions, comments, concerns and criticisms always welcome,

Wednesday, March 8, 2017

Hackathon week

This is a hackathon week for us here at Tableau.  We get to work on self-directed projects, things we "know need to get done", explore new avenues and so on.  Overall, it is a very productive time and most folks look forward to this regular activity.

For this week, I am working on a simple (think V1) "fuzzing" tool to dirty the Superstore sample workbook we ship.  In this context, fuzzing means to dirty some of the fields in the data to reflect bad data that may get sent to Tableau.

An example of this is a storm damage spreadsheet I encountered while taking a data cleaning class.  This spreadsheet (since it is still in use to my knowledge I won't link to it) was a hand created list of major storms in the USA dating back to the 1950s.  When I hear the phrase "hand created" I have learned to shudder.  There is simply the potential for a type to creep in and skew the data (at best) or result in completely invalid entries (much worse).  For instance, this particular spreadsheet had 54 states listed.

A fair question to ask would be "How many states would I expect to see?"  50 is an obvious answer, since we currently have 50 states.  But that wasn't true when the data was first starting to be collected.  But we have 50 now, so 50 might be a reasonable first guess.  The District of Columbia is not a state, but is often included in data gathered by the government, so 51 "states" may also be a good expectation.

Since this is storm data, I naturally include hurricanes in this and that makes me think of the Caribbean.  Now I have to wonder if I need to include Puerto Rico in my analysis, which would raise the state count to 52.

Finally, now that I had some expectation about 50 not necessarily being the absolute accurate value I expected I began to dig through the data.  In this case, DC was included, but Puerto Rico was not.  The other three "states" were typos - things like "Florida" being spelled "Flarida" and similar.  (Quick - how many ways can you type valid names or abbreviations for "Florida"?) Once I had that cleaned up, I was down to 51 states (50 states + the District of Columbia) for my analysis.

Anyway, remembering that lesson made me want to contribute to this effort.  Data is not always (never?) clean to start.  This hackathon I am trying to help solve that challenge.

Questions, comments, concerns and criticisms always welcome,

Monday, February 27, 2017

Expanding unit tests for Tabpy - the Python connector for Tableau

Now that Tabpy has a unit test framework (thanks Bora!)  I decided to add a few unit tests to check for error handling in the event of malformed URLs and such.  This was a relatively straightforward task and, like many test tasks, uncovered a defect in the code.

In this case, we allow for a timeout setting - a set number of seconds to wait before "giving up" (I'm simplifying here).  The documentation for that command said that the number of seconds you tell it to wait cannot be None.  When I was looking at the unit tests here, I saw there was no check for None so I added it.  Then I thought about it a little more and realized that I could pass in a negative amount of seconds to wait.  That makes no sense at all, so I also added a check for that condition.  So at about line 90 (in current versions) of \tabpy-client\client.py, you can see the code I added:

        if query_timeout is not None and query_timeout > 0:
            self.query_timeout = query_timeout
            self.query_timeout = 0.0

The unit test for that change is also pretty simple:

        client = Client(


If you haven't installed Tabpy feel free to hop over there and give it a shot.  It's also now available on Pipy so you can pip install tabpy if you want.

And please add some more testing if you like!

Questions, comments, concerns and criticisms always welcome,

Tuesday, February 21, 2017

New team name!

For a while now, we have been contemplating renaming our team here at Tableau.  We have (had) been the Statistics team since I started.  While that is a great name, it really did not reflect our day to day work.  We use statistics for all that we do, but we don't create new algorithms or anything like that.  It would be roughly akin to calling a team that works on cars the Algebra team if you use algebra to compute mileage or whatever.

So after thinking about it for almost a year (!) we finally settled on the Advanced Analytics team as our name.  We're still getting used to it and I hope it starts to roll of the tongue easily as time goes by.  Our mission is the same - to help you see and understand your data - so the day to day work doesn't change.  But it does help define our role a little better for both internal teams and even when we partner with outside folks.

So here's to Advanced Analytics!

Questions, comments, concerns (and a cool nickname) always welcome,

Wednesday, February 15, 2017

Some comments about comments in code

I was looking at a code review recently and was reminded of the old piece of advice I once received:  "God comments in code do not explain what the code does, they explain why the code does what it does."

I agree completely.  I was looking at some code to validate we use the correct font.  The code would change from normal, to Light, to Bold, etc… and preceding each of those commands was a comment that said "Changing to Light", "Changing to Bold, " etc..  For a simple task like this it is not too hard to figure out why the font is being changed.  In this case, we want to validate the Bold font, the Light font and so on.  But when code gets trickier, the why the code exists becomes much more important.

Oh, one other thing that drives me up a wall.  Typos in comments.  Seriously - if I am looking for the word "Bold" then I want to find that word.  If someone had spelled it "Blod" then search would fail and life can become quite difficult if I need to change the Bold font behavior everywhere it is used.  Fortunately, this doesn't happen very often, but when I see it, I generally try to make a code change to spell the word correctly. 

Questions, comments, concerns and criticisms always welcome,

Tuesday, February 7, 2017

Snow days mean working from home

While I am a big fan of snow days in general, this week in Seattle has been tough.  It snowed surprisingly hard for a while and I was unable to make it to our offices on Monday.

While this is not a terrible situation, it meant I had to work from home for the day.  I'm not personally a big fan of working from home.  There are several reasons for this and the biggest is probably that "home is where I go when I am done working."  Mentally, it is tough for me to stay focused when I am home.

Another reason is the limited hardware I have at home, specifically multiple monitors.  I'm lucky enough to have 2 monitors at my desk (three if you count my laptop) at work.  That makes debugging and testing in general much easier - run Tableau on one monitor, debug on the other.  I've written a few articles about this in the past and  I do not have this at home.  Typing on one monitor is simply frustrating for the work I do.

I also miss the interaction with my team.  Sometimes I just need a simple question answered which is pretty fast if folks are around me.  At home, I have to rely on instant messaging, email or whatever and the delay can leave me blocked for a while.

Finally, my home network is not as fast as our work building.  Frustrating.

The task I CAN do at home is paperwork.  Updating documentation, reading designs, giving feedback, etc… are the types of work I can complete at home and not feel distracted.  As luck would have it, I had about a day's worth of reading and online training to complete while snowed in, so the day went pretty well.

Now, back to the office and (real) work!

Questions, comments, concerns and criticisms always welcome,

Monday, January 30, 2017

Getting Good Estimates

We use Microsoft Team Foundation Server for tracking our activities during our sprints.  You can jump over there to read up a little about this if you have never seen it.  One of the areas that we are focusing currently is estimating how much work we can realistically get done in our two week sprints.

We started tracking our work to the half day level a few months ago.  As an aside, there are several schools of thought on how to track this type of activity.  You can track by the hour or the day is one example, and there is even a difference of opinion for how to track items that will take more than 1 day.  Some folks say just use the best estimate you have, and others say always round up, and even debates about using the Fibonacci sequence for scheduling.

Where we have focused has been on two areas:
  1. Simply creating a task for everything we need to do.
    1. An example here is a couple of our folks are working on documentation for a new feature we are trying to start.  Each of them devoted X number of days to get the documentation done.  So far, so good.
    2. The problem was that the entire team needs to read, review and respond to the documentation.  We had not devoted any scheduled time to that work in the past.  If we each need 1 or 2 days, that is a large percentage of a 2 week cycle.  We learned our lesson in the past so we devote dedicated time here.
    3. Other examples would be blocking time for code reviews (more time if we know a particularly complex change will be attempted in the sprint), time for hackathons, etc…
  2. Once we have those estimates, TFS gives us a summation of how much time we have and how much work we are adding to a sprint.  If we have 100 engineer days, for example, we should not try to take on 120 days of work. 
    1. We have tried this in the past and it seldom works.  The extra work simply gets carried over to the next sprint, but having that long list makes prioritizing difficult. 
    2. Plus, if we have a work item that partner teams need done, the state of it should accurately reflect when a change would be expected.  By pulling it into the current 2 week cycle, stakeholders may reasonably expect a change within 2 weeks.  We have worked to avoid this situation.

So now we have the correct number of work items, all with our best estimates attached on the first day of the sprint.  We take the highest priority items in first.  (This assumes we have an accurately prioritized back log of work items, which we have).  So far our burndown charts show that we have decreased our range of estimates by about 75% - we are 75% more accurate now than 2 months ago.  We'll keep iterating to get this number more accurate over time.

Questions, comments, concerns and criticisms always welcome,

Tuesday, January 24, 2017

Sprint Planning week

We follow an Agile process here at Tableau and one of my roles in that is scrum master.  Those links are pretty good overview sites if this is not familiar to you.  Jump over there and read a little about this if you want.

We follow a 2 week sprint cycle here and this was our planning week.  Nothing too out of the ordinary with this - we decide what work items we will complete in the next 2 weeks, assign out some of them to get started and decide what we will demonstrate  at the end of the cycle.  The hardest part of this is usually deciding what to demo to everyone who is interested.  In this cycle, we want to demo a tool one of the testers wrote that generates random data that follows a desired distribution, and we have some cluster work we also want to show.

For me, the hardest part as scrum master is usually trying to find a room in which to meet :)  We moved into a new building at the beginning of the month so getting a room this time was a bit easier since the rooms are mostly unbooked right now. 

For the next 2 weeks we will work on the items we decided to do, update our progress daily and meet daily at standups.  One new tactic we are trying for standup meetings is to update per project item, rather than by person.  In the past, each person on the team has provided an update for what has happened for that person in the last 24 hours, but we are changing that to be more project based.  So far it is going well.

Again, nothing earth shattering here but I wanted to give a very broad overview into a typical day at Tableau.

Questions, comments, concerns and criticisms always welcome,

Monday, January 16, 2017

Not making a change that would have made no sense

Like most applications, Tableau supports several languages.  Here's a quick screenshot of what we currently offer:

No big surprise here: we keep a list of all the text (strings) that we use in our application in a file, and localize (translate) them into each language. 

For instance, we have the color "Blue" in our UI in many places.  In Spanish, that will be "Azul".  So we have a file that has a string number (let’s say "String1447") and that string number points to several translations of "Blue." 

What we try to do - and this is pretty much the standard way of providing multiple language support - is to never refer to the text "blue".  Instead, whenever we want that word to show, we ask for the correct translation of "String1447" - which in English will be "Blue," in Spanish "azul," etc… 

So if I am looking at a color palette on an English machine, when I hover over the Blue color, the UI asks for the English translation of "String1447" and shows me "Blue." 

I was looking through our string table the other day to see if there was any way to make the process of using it more efficient.  One thing that jumped out at me was that we have several different entries for "Blue" (and "Green" and "Brown" and other colors).  That struck me as odd - why do we need to have all these duplicates?  If we just had one string ("String1447") for Blue, we could have smaller files, less build time, less confusion with knowing which string to use and so on.

So my first thought was to remove all the duplicated strings.

When I looked a little deeper though, I noticed that each color palette we have has its own list of colors.  An example would be the Summer palette.  Blue is near the bottom on my machine:

Another instance would be the Tableau 10 colors:

In the strings file, each color palette has its own list of colors.  There are 8 colors listed for Summer and 10 for Tableau 10 which makes sense.  If I am working with the Summer palette, I would naturally want to use a string name of "Summer_Blue" or similar, naturally want to refer to "String_Summer_Blue" or similar.  And if I was working with the Tableau_10 color palette, I would want to use "String_Tableau_10_Blue". 

My original plan was to replace these with something like "String_Just_Blue" (heh) or "String_Blue" and simply use this string everywhere that we need.  But that plan was shelved pretty quickly since it would make our strings table hard to read.  If the list of 10 colors for the "Tableau 10" palette only listed 6 (because of duplicate strings being removed) then everyone would find it confusing.

So my investigation into making files smaller here would have resulted in a system that is more confusing than what we currently have.  So I am not making this change and moving on to other work.

Questions, comments, concerns and criticisms always welcome,