Why Developers Never Use State Machines

A few months ago I saw a great little blog post about state machines on the Shopify blog. The message was that state machines are great and developers should use them more – given my recent experiences with state machines at CrowdHired, I could certainly agree with that. But it got me thinking, how many times in my developer career have I actually used a state machine (either separate library or even hand-rolled abstraction)? The answer is zero times – which surprised the hell out of me since state machines really are very useful. So I decided to engage in a bit of introspection and figure out why we tend to manage our “state” and “status” fields in an ad-hoc fashion rather than doing what is clearly called for.

We Don’t Need One Until We Do

The problem is that you almost never create an object fully formed with all the behaviour it is ever going to need, rather you build it up over time. The same is true for the “states” that a state machine candidate object can be in. So, early on you don’t feel like your objects’ state machine behaviour is complex enough to warrant a “full-blown” state machine (_YAGNI and all that jazz), but later on – when it IS complex enough – you feel like you’ve invested too much time/effort to replace it with something that has equivalent functionality. It’s a bit of a <a href=“http://en.wikipedia.org/wiki/Catch-22(logic)” target=”_blank”>catch-22. It’s overkill and by the time it’s not, it’s too late.

A State Machine Is A Fluffy Bunny (Not Particularly Threatening)

Bunny

Those of us who went through computer science degrees remember state machines from our computing theory subjects and the memories are often not fond ones. There are complex diagrams and math notation, determinism and non-determinism, Moore and Mealy, as well as acronyms galore (DFA, NFA, GNFA etc.). We come to believe that state machines are more complex than they actually are and it is therefore nothing but pragmatism that makes us consider a “full-blown” state machine overkill.

But most state machines you’re likely to need in your day-to-day development have nothing in common with their computing theory counterparts (except the … errr … theory). You have states which are strings, and events which are methods that cause transitions from one state to another – that’s pretty much it (at least for the state_machine gem in Ruby). The point is, even if you have two states, a state machine is not overkill, it might be easier that rolling an ad-hoc solution, as long as you have a good library to lean on.

Even A Good Tool Is Not A Good Tool

I would hazard a guess that there are decent state machine libraries for most languages that you can use (the aforementioned state_machine for Ruby is just one example). But even a fluffy bunny has a learning curve (I am stretching the metaphor well past breaking point here). That wouldn’t be such an issue if you were solving a problem, but all you’re likely doing is replacing an existing solution. Since we tend to turn to a state machine library after the fact (our ad-hoc solution is working right now). Just like with everything that has “potential future benefits” the immediate value is very hard to justify even to yourself (unless you’ve had experience with it before). The slight learning curve only tips the scale further towards the “we can live without it” side. It doesn’t matter how good a tool is if you never give it a chance.

It is really difficult to appreciate (until you’ve gone through it) – how much better life can be if you do give a good state machine library a chance. When we finally “bit the bullet” at CrowdHired and rejigged some of our core objects to use the state_machine gem, the difference was immediately apparent.

  • Firstly the learning curve was minor, I did spend a few hours of going through the source and documentation, but after that I had a good idea what could and couldn’t be done (I might do an in-depth look at the state_machine gem at some point). 
  • The integration itself was almost painless, but moving all the code around to be inline with the new state machine was a big pain. In hindsight had we done this when our objects only had a couple of states it would have been a breeze
  • We’re now able to easily introduce more states to give our users extra information as well as allow us to track things to a finer grain. Before it was YAGNI cause it was a pain, now we find that we “ai gonna need” after all, cause it’s so easy.
  • Our return values from state transitions are now 100% consistent (true/false). Before we were returning objects, arrays of objects, nil, true/false depending on who was writing it and when. 
  • We’re now able to keep an audit trail of our state transitions simply by dropping in statemachine-audit_trail (_see that Shopify post), before it was too hard to hook it in everywhere so we had nothing.
  • We removed a bunch of code and improved our codebase – always worthy goals as far as I am concerned.

My gut-feel is that most people who read that Shopify post agreed with it in spirit, but did nothing about it (that’s kinda how it was with me). We seem to shy away from state machines due to misunderstanding of their complexity and/or an inability to quantify the benefits. But, there is less complexity than you would think and more benefits than you would expect as long you don’t try to retrofit a state machine after the fact. So next time you have an object that even hints at having a “status” field, just chuck a state machine in there, you’ll be glad you did. I guarantee it or your money back :).

Image by tfangel

When Developers Go To Great Length To Save Typing 4 Letters

Great Lengths

Heroku is a great platform. Long before I joined and when I say long, I mean in startup terms (i.e. a few weeks before I joined :)) – the decision was made that CrowdHired would be hosted on Heroku. Shortly after I came on board, Heroku released their new Cedar stack and we quickly migrated across to that. I find it kinda amusing that we’re currently in alpha, deploying to a platform that’s in beta. Latest and greatest FTW. While migrating to the new stack we also settled on Thin as our web server. The Cedar stack allows you to use whatever web server you want in production and will run on Webrick by default – not ideal. Since we were going to use Thin in production it made sense that we’d also use it in development instead of Webrick.

When you’re using Rails, swapping a new web server in is pretty painless. Just include the gem and then use the rails s command to launch your new server e.g

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
rails s thin```

Pretty simple right? Well, it is, but I am very used to typing `rails s` to launch my server and no matter what gem you include in your project, `rails s` still starts Webrick (_this is not entirely accurate but bare with me_). Muscle memory being what it is, after typing `rails s` instead of `rails s thin` a couple of times (_and only realising this a few hours later_) I decided to see if I could make Thin the default for the `rails s` command.

## Digging Into The Rails Command Line

The key thing here was to figure out how `rails s` actually works &#8211; only way to do that is to <a href="http://www.skorks.com/2010/05/why-i-love-reading-other-peoples-code-and-you-should-too/" target="_blank">read some code</a>. We know that there is a `script/rails` executable that lives in all our Rails project so it makes sense that this would be the entry point into figuring out `rails s`, but in reality it's not (_or at least it's not that simple_). We don't actually type `script/rails s`, we do `rails s`, so **there must be an executable called `rails` within the Rails gem itself** which is declared as such in rails' gemspec. This is indeed the case, it looks like this:

```ruby
#!/usr/bin/env ruby
 
if File.exists?(File.join(File.expand_path('../..', __FILE__), '.git'))
  railties_path = File.expand_path('../../railties/lib', __FILE__)
  $:.unshift(railties_path)
end
require "rails/cli"

But even that is not the start of the story. Apparently, when you have an executable in a gem, rubygems will not use it as is, but will generate another executable which wraps your one. For the rails command it looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'rails' is installed as part of a gem, and
# this file is here to facilitate running it.
#
 
require 'rubygems'
 
version = "&gt;= 0"
 
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
  version = $1
  ARGV.shift
end
 
gem 'rails', version
load Gem.bin_path('rails', 'rails', version)

This is the true entry point you hit when you type rails s. Of course, all this does is load/call the original executable from the Rails gem.

The Rails source is broken up into several projects such as activerecord, activesupport etc. Probably the most important one of these is railties. This is where the rails executable takes us. Of course, before it does that it needs to put the lib/ folder of the railties project on the load path, but eventually we end up in railties/lib/rails/cli.rb. Here we almost immediately execute the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Rails::ScriptRailsLoader.exec_script_rails!```

All this does is essentially figure out if we're inside a Rails app and if we are it executes `script/rails` passing through the command line arguments that you supply. So, we're now back in our Rails app; `script/rails` is the real entry point after all (_although we're about to be taken straight back to **railties**_). The file looks like this:

```ruby
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
 
APP_PATH = File.expand_path('../../config/application',  __FILE__)
require File.expand_path('../../config/boot',  __FILE__)
require 'rails/commands'

We require boot.rb so that we can hook into Bundler and make sure the relevant gems are on the load path (such as rails for example), we then jump into railties/rails/lib/commands.rb. Here everything is pretty straight forward, we have a big case statement which has a clause for “server”. We instantiate a new Rails::Server and start it, which tells us very little by itself, but if we jump into railties/rails/lib/commands/server.rb we can see that Rails::Server simply extends Rack::Server (and delegates to Rack::Server&#39;s start method from its start method) all it adds is those familiar lines we’re all used to seeing e.g.:

1
2
3
4
=&gt; Booting Thin
=&gt; Rails 3.0.7 application starting in development on http://0.0.0.0:3000
=&gt; Call with -d to detach
=&gt; Ctrl-C to shutdown server

So, if we want to change which server is picked up by default when we type rails s we need to go look in Rack.

A Quick Glance At Rack

Luckily we can easily grab it from Github and crack it open (you have to admire the Ruby open source ecosystem, it is truly awesome, largely due to Github, so big props to those guys). We of course need to check out lib/rack/server.rb where we find the following method:

1
2
3
def server
  @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
end

So, if we don’t pass in the name of the server we want, Rack::Handler.default will try to determine it for us.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def self.default(options = {})
  # Guess.
  if ENV.include?("PHP_FCGI_CHILDREN")
    # We already speak FastCGI
    options.delete :File
    options.delete :Port
 
    Rack::Handler::FastCGI
  elsif ENV.include?("REQUEST_METHOD")
    Rack::Handler::CGI
  else
    begin
      Rack::Handler::Mongrel
    rescue LoadError
      Rack::Handler::WEBrick
    end
  end
end

As you can see, it turns out that the real default server is actually Mongrel. So if you included Mongrel in your Rails project, typing rails s would automatically pick that up without you having to do anything. Only if Mongrel fails, do we fall back to Webrick which is part of Ruby and therefore is always present. So what do we do if we want Thin to be one of the defaults? Well, first thing first, we need to check if Rack includes a handler for Thin. If we look in lib/rack/handlers/ we can see that Rack itself includes the following:

1
2
3
4
5
6
7
8
9
cgi.rb
evented_mongrel.rb
fastcgi.rb
lsws.rb
mongrel.rb
scgi.rb
swiftiplied_mongrel.rb
thin.rb
webrick.rb

Luckily Thin is there, so what we really want is that default method to look something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def self.default(options = {})
  # Guess.
  if ENV.include?("PHP_FCGI_CHILDREN")
    # We already speak FastCGI
    options.delete :File
    options.delete :Port
 
    Rack::Handler::FastCGI
  elsif ENV.include?("REQUEST_METHOD")
    Rack::Handler::CGI
  else
    begin
      Rack::Handler::Thin
    rescue LoadError
      begin
        Rack::Handler::Mongrel
      rescue LoadError
        Rack::Handler::WEBrick
      end
    end
  end
end

This way Thin will be the first default, followed by Mongrel and only then falling through to Webrick. Luckily, since we’re using Ruby, we can reopen the class and replace the method with our version. I think this is a perfect example where the ability to reopen classes comes in extremely handy, without any need to worry about “scary consequences.

Getting It Working

All we really need to figure out is where to put the code that reopens the class so that it gets picked up before Rails tries to launch the server. The only logical place is the script/rails executable itself, which will now look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
 
APP_PATH = File.expand_path('../../config/application',  __FILE__)
require File.expand_path('../../config/boot',  __FILE__)
require 'rack/handler'
Rack::Handler.class_eval do
  def self.default(options = {})
    # Guess.
    if ENV.include?("PHP_FCGI_CHILDREN")
      # We already speak FastCGI
      options.delete :File
      options.delete :Port
 
      Rack::Handler::FastCGI
    elsif ENV.include?("REQUEST_METHOD")
      Rack::Handler::CGI
    else
      begin
        Rack::Handler::Mongrel
      rescue LoadError
        begin
          Rack::Handler::Thin
        rescue LoadError
          Rack::Handler::WEBrick
        end
      end
    end
  end
end
require 'rails/commands'

This works without any problems, we type rails s and as long as Thin is in our Gemfile it starts up by default. As an aside, notice that I used class_eval to reopen Rack::Handler. Metaprogramming tricks like this should be part of every Ruby developer’s toolbox, I’ll talk more about this some other time (seeing as I am well into TL;DR land here).

Going through this exercise didn’t take long (under 30 minutes) and taught me a bit more about Rails and Rack. Shortly after doing this – in a curious case of the universe working in interesting ways – I came across a Stackoverflow question asking about this exact scenario and got an inordinate amount of satisfaction from being able to easily answer it :). In-fact, even the fact that shortly after Jason found Pow and we switched over to that, doesn’t really diminish the satisfaction of quickly solving a seemingly difficult problem in a neat way. The lesson here is this, no matter what problems you come across don’t automatically look for a library to handle it. Do spend a few minutes investigating – it might be enough to solve it (especially if you’re using Ruby) and you’ll certainly learn something.

Image by Gerard Stolk presque 64

Even Boring Form Data Can Be Interesting (For A Developer)

Bored

What could be more boring than capturing credit card data on a form? Well, it’s actually not that boring since you may want to encrypt this particular data, which presents it’s own set of challenges. Nevertheless, it’s still a textbox which takes digits that you store in a database – whoopty doo – not exactly rocket surgery. Well, I’ve got a piece of data that’s got the credit card beat for sheer mundanity – the ABN. If you’re an Australian you know all about this. For everybody else, it stands for Australian Business Number which is an 11 digit number, provided by the government to every company. It’s not secret (_you can look them up online_), so you don’t even need to encrypt it – difficult to get excited about that. Of course if that was the end of the story, this wouldn’t be much of a blog post, so – as you might imagine – things are not as bland as they appear.

The Interesting Thing About A Credit Card Number…

At CrowdHired, we don’t tend to deal much with credit card numbers, but ABNs are another matter entirely – since companies are one of the two types of users we have in the system (by the way – as you may have deduced – I’ve been working for a startup for the last few months, I should really talk about how that happened, it’s an interesting story). Just like any piece of data, you want to validate the user input if you possibly can. When I started looking into this for ABNs I discovered that they had an interesting trait, it is a trait which credit card numbers share. You see, both credit cards and ABNs are self-verifying numbers.

I’ve been doing web development for many years now, but had no idea this was the case. So naturally – being the curious developer that I am – I had to dig a little further. It turns out that these kinds of numbers are quite common, with other well-known examples being ISBNs, UPCs and VINs. Most of these use a variation of a check digit-based algorithm for both validation and generation. Probably the most well-known of these algorithms is the Luhn algorithm which is what credit cards use. So, we’ll use a credit card as an example.

Validating And Generating Credit Cards (And Every Other Check-Digit Based Number)

Let us say we have the following credit card number:

1
2
3
4870696871788604```

It is 16 digits (_Visa and MasterCard are usually 16, but Amex is 15_). This number is broken down in the following way:

Issuer Number | Account Number | Check Digit 487069 | 687178860 | 4

1
2
3
You can read lots more about the anatomy of a credit card, but all we want to do is apply the Luhn algorithm to check if this credit card is valid. It goes something like this:

**1. Starting from the back, double every second digit**

4 | 8 | 7 | 0 | 6 | 9 | 6 | 8 | 7 | 1 | 7 | 8 | 8 | 6 | 0 | 4 8 | 8 |14 | 0 |12 | 9 |12 | 8 |14 | 1 |14 | 8 |16 | 6 |00 | 4

1
**2. If the doubled numbers form a double digit number, add the two digits**

4 | 8 | 7 | 0 | 6 | 9 | 6 | 8 | 7 | 1 | 7 | 8 | 8 | 6 | 0 | 4 8 | 8 |14 | 0 |12 | 9 |12 | 8 |14 | 1 |14 | 8 |16 | 6 |00 | 4 8 | 8 | 5 | 0 | 3 | 9 | 3 | 8 | 5 | 1 | 5 | 8 | 7 | 6 | 0 | 4

1
**3. Sum up all the digits of this new number**

8+8+5+0+3+9+3+8+5+1+5+8+7+6+0+4 = 80```

4. If the number is perfectly divisible by 10 it is a valid credit card number. Which in our case it is.

You can see how we can use the same algorithm to generate a valid credit card number. All we have to do is set the check digit value to X and then perform all the same steps. During the final step we simply pick our check digit in such a way as to make sure the sum of all the digits is divisible by 10. Let’s do this for a slightly altered version of our previous credit card number (we simply set the digit before the check digit to 1 making the credit card number invalid).

1
2
3
4 | 8 | 7 | 0 | 6 | 9 | 6 | 8 | 7 | 1 | 7 | 8 | 8 | 6 | 1 | X
8 | 8 |14 | 0 |12 | 9 |12 | 8 |14 | 1 |14 | 8 |16 | 6 | 2 | X
8 | 8 | 5 | 0 | 3 | 9 | 3 | 8 | 5 | 1 | 5 | 8 | 7 | 6 | 2 | X
1
2
3
8+8+5+0+3+9+3+8+5+1+5+8+7+6+2+X = 78+X
X = (78%10 == 0) ? 0 : 10 - 78%10
X=2

As you can see no matter what the other 15 digits are, we’ll always be able to pick a check digit between 0 and 9 that will make the credit card number valid.

Of course not every self-verifying number uses the Luhn algorithm, most don’t use mod(10) to work out what the check digit should be, and for some numbers like the IBAN, the check digit actually consists of 2 digits. And yet, the most curious self-verifying number of the lot is the first one I learned about – the ABN. This is because, for the life of me, I couldn’t work out what the check digit of the ABN could be.

The Curious Case Of The ABN

Curious

Australia is certainly is not averse to using check digit-based algorithms. The Australian Tax File Number (TFN) and the Australian Company Number (ACN) are just two examples, but the ABN seems to be different. At first glance the ABN validation algorithm is just more of the same, it just has a larger than normal “mod” step at the end (mod(89)).

  • Subtract 1 from the first (left) digit to give a new eleven digit number
  • Multiply each of the digits in this new number by its weighting factor
  • Sum the resulting 11 products
  • Divide the total by 89, noting the remainder
  • If the remainder is zero the number is valid

In-fact, here is some ruby code to validate an ABN which I appropriated from the Ruby ABN gem (and then rolled it into a nice Rails 3, ActiveRecord validator so we could do validates_abn_format_of in all out models :)) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def is_integer?(number)
  Integer(number) 
  true 
rescue 
  false
end
  
def abn_valid?(number)
  raw_number = number
  number = number.to_s.tr ' ', ''
  return false unless is_integer?(number) && number.length == 11

  weights = [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
  sum = 0
  (0..10).each do |i|
    c = number[i,1]
    digit = c.to_i - (i.zero? ? 1 : 0)
    sum += weights[i] * digit
  end

  sum % 89  == 0 ? true : false
end

But, while validating ABNs is easy, generating them is a whole other matter. As we’ve seen, with a check digit-based algorithm, generating the number is the same as validating the number, except we pick the digit in such a way as to make our ‘mod’ step evaluate to zero. But with a number such as the ABN, where there is no apparent check digit (perhaps I am just having a bout of stupid, so if you can see an obvious check digit with ABNs do let me know), how do you easily generate a valid number? In-fact, why would you want to generate these numbers in the first place, isn’t being able to validate them enough?

Well, in the case of CrowdHired, we tend to create object trees that are quite deep, so we build an maintain some infrastructure code to allow us to create fake data for use during development (another interesting thing to talk about at a later date). Before we started using the self-validating properties of ABNs we simply generated any old 11 digit number as fake data for ABN fields, but once the validations started kicking in this was no longer an option. Being the pragmatic developers that we are (even if we do say so ourselves), we took some real ABNs (like our own) chucked them into an array and randomly picked from there. But, this offended the developer gods, or my developer pride – whichever, so one Saturday I decided to take a couple of hours to generate some truly random ABNs that were still valid. Here is the code I came up with (it is now a proud part of our fake data generation script):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def random_abn
  weights = [10,1,3,5,7,9,11,13,15,17,19]
  reversed_weights = weights.reverse
  initial_numbers = []
  final_numbers = []
  9.times {initial_numbers &lt;&lt; rand(9)+1}
  initial_numbers = [rand(8)+1, rand(7)+2] + initial_numbers
  products = []
  weights.each_with_index do |weight, index|
    products &lt;&lt; weight * initial_numbers[index]
  end
  product_sum = products.inject(0){|sum, value| sum + value}
  remainder = product_sum % 89
   if remainder == 0
    final_numbers = initial_numbers
  else
    current_remainder = remainder
    reversed_numbers = initial_numbers.reverse
    reversed_weights.each_with_index do |weight, index|
      next if weight &gt; current_remainder
      if reversed_numbers[index] &gt; 0
        reversed_numbers[index] -= 1
        current_remainder -= weight
        if current_remainder &lt; reversed_weights[index+1]
          redo
        end
      end
    end
    final_numbers = reversed_numbers.reverse
  end
  final_numbers[0] += 1
  final_numbers.join
end

The idea is pretty simple. Let’s go through an example to demonstrate:

1. Firstly we randomly generate 11 digits between 0 and 9 to make up our probably ABN (they are actually not all between 0 and 9 but more on that shortly)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
7 5 8 9 8 7 3 4 1 5 3```

**2. We then perform the validation steps on that number**

  * multiply the digits by their weights to get weight-digit products&nbsp;
    
    ```
7x10=70
      5x1=5
      8x3=24
      9x5=45
      8x7=56
      7x9=63
      3x11=33
      4x13=52
      1x15=15
      5x17=85
      3x19=57
    ```

  * sum the products `70+5+24+45+56+63+33+52+15+85+57 = 505`
  * divide by 89 to get the remainder `505 mod 89 = 60`

**3. Since we do `mod(89)` at worst we'll be off by 88 (_although if we get 0 as the remainder we lucked out with a valid ABN straight away_), we now use the weight-digit products to "_give change_", subtracting from the remainder as we go until we hit zero.**

We start with the last digit where the weight is 19. We subtract 1 from this digit, which means we can subtract 19 from our remainder. We then move on to the next digit until the remainder hits zero <span class="Apple-style-span" style="font-family: monospace; white-space: pre; "></span>

Initial | Change | Remainder

7x10=70 | 7x10=70 | 0 5x1=5 | 5x1=5 | 0 8x3=24 | 8x3=24 | 0 9x5=45 | 9x5=45 | 0 8x7=56 | 8x7=56 | 0 7x9=63 | <strong>6</strong>x9=63 | 0 3x11=33 | 3x11=33 | 9 4x13=52 | 4x13=52 | 9 1x15=15 | 0x15=0 | 9 5x17=85 | <strong>4</strong>x17=68 | 24 3x19=57 | <strong>2</strong>x19=38 | 41

1
**4. This gives us our new number**

7 5 8 9 8 6 3 4 0 4 2```

5. Now we just need to add 1 to the very first number (as per the ABN validation steps) and we have our valid ABN

85898634042

There are a couple of nuances to those steps.

  • None of our initial generated digits can be zero. Since we “give change” by subtracting 1 from each digit we need to ensure we can actually subtract 1 (otherwise things become much more complex). So we ensure our digits are randomly generated between 1 and 9 rather tahn between 0 and 9.
  • Even when all our initial digits are at least 1 we may still fail to “give change” for some of the remainders, the easiest example of this is when our remainder is 2. The only digit when can use to give change is the one with the weight of 1 (i.e. the second digit of the ABN). If this digit is initially generated to be 1, we can only give change once at which point we end up with a remainder of 1 and there is nothing more we can do about it. In-fact this exact scenario can happen for a few other remainders namely 86, 77, 66, 53, 38, 21. The easies way to overcome this problem is to ensure that the digit that has a weight of 1, is always randomly generated to have a value of at least 2. This way we can use it to give change twice and our problematic remainders are covered.
  • Lastly, since we have to add 1 to the very first digit as our final step, we need to make sure that this digit is not already equal to 9, so we generate that one to be between 1 and 8.

Given these nuances, this algorithm won’t generate every possible ABN, but it will give you a large percentage of possible ABNs which is good enough for our needs. It took about an hour to get that working (we won’t mention the little bug where I forgot the remainder could be zero from the start, which caused much grief to our random data generator :)), but it was a fun little exercise – time well spent as far as I am concerned. And to think, all this learning about self-validating numbers and algorithmic coding fun was triggered by trying to capture the most mundane piece of data on a form. It just goes to show that you can learn and grow no matter where you are and what you’re doing, you just need to see the opportunities for what they are.

Images by Sn.Ho and Sangudo