Irrational Exuberance!

Modeling a hiring funnel with Systems library.

September 18, 2018. Filed under systems-thinkinghiring

After writing the Introduction to systems thinking, I scheduled my yearly trip through the internet looking for easy to use and reasonably priced systems modeling software, and didn't quite find what I wanted.

Aftewards, I decided to do an experiment in defining a text format for models, loosely inspired by Graphviz, and build some tools for running those models. That experiment culminated in the systems library for Python, which I'll use here to model a hiring funnel.

We'll start with a very simple model, and continue layering on concepts until we have something interesting. Installation instructions are here, but for most cases you can install via Python's pip module:

python3 -m pip install systems

Once you have systems install, we can start writing our model, starting with the simplest definition:

[Candidate] > Prospect    @ 10
Prospect    > PhoneScreen @ 0.5
PhoneScreen > Onsite      @ 0.5
Onsite      > Offer       @ 0.25
Offer       > Accept      @ 0.5
Accept      > Hired       @ 1.0

Breaking this down into a few interesting details. Each line represents two stocks and one flow.

Prospect    > PhoneScreen @ 0.5    

For example, the line above shows the the Prospect stock and the PhoneScreen stock. There is a 50% conversion flow between them, such that each round the entire value of Prospect will be multiplied by 50% and that value will be added to PhoneScreen. Prospect itself will be zeroed out by the conversion, but in this case it'll be refilled immediately by the flow from Candidate:

[Candidate] > Prospect    @ 10

Stocks surrounded by brackets, such as [Candidate], are infinite stocks, typically used as the entry or exit of a system. These stand to be stand-ins for some other system that you're not modeling right now. This flow, represented by the whole number 10, will remove 10 from the Candidate stock and add 10 to Prospect. If the source stock has less than ten, the flow will move up to ten instead, although in this case the source is infinite so that's not likely to happen.

Assuming we have written our model in hiring.txt, we can run the model via:

cat hiring.txt | systems-run

Which will run the model for ten rounds, showing this output:

        Prospect        PhoneScreen     Onsite  Offer   Accept  Hired
0       0               0               0       0       0       0
1       10              0               0       0       0       0
2       10              5               0       0       0       0
3       10              5               2       0       0       0
4       10              5               4       0       0       0
5       10              5               2       1       0       0
6       10              5               4       1       0       0
7       10              5               2       2       0       0
8       10              5               4       0       1       0
9       10              5               2       1       0       1
10      10              5               4       1       0       1

A few things to note. First, the infinite [Candidate] stock is not rendered, because the state of infinite stocks isn't very interesting. Second, that getting folks hired takes a long time! Finally, note that converion flows require that their source reach a high enough number than the output is a whole number, which means that you see some buffering across rounds in Onsite or Offer as their preceeding flows' inputs get to 4 and 2 respectively.


This model isn't quite as realistic as it could be though, for example, we don't really get ten new prospects each day, it depends on how many sourcers are sourcing. Also, we're probably hiring more recruiters as well to scale until we run out of recruiting headcount! We can model the second bit by creating a new stock Recruiters stock that has an initial value of 3 and a maximum of 8, assuming that eight is your headcount available for recruiters.

[Candidate] > Recruiters(3, 7) @ 1

In round zero we'll have three recruiters, and we'll add one each round until we have seven recruiters, at which point we'll stop.

More interesting, we can use the value of the Recruiters stock to determine the number of prospects we find each round. For example, we could say that each recruiter will source ten prospects per round:

[Candidate] > Prospect @ Recruiters * 10

In the first round we'll get thity prospects, forty in the second round, up to seventy in the last round. Now when we run the model:

cat hiring.txt | systems-run

We see these results (hiding the Hires column so the results fit on screen more easily):

        Recruiter      Prospect        PhoneScrn       Onsite  Offer   Accep
0       3               0               0               0       0       0
1       4               30              0               0       0       0
2       5               40              15              0       0       0
3       6               50              20              7       0       0
4       7               60              25              10      1       0
5       7               70              30              12      3       0
6       7               70              35              15      3       1
7       7               70              35              17      3       1
8       7               70              35              17      4       1
9       7               70              35              17      4       2
10      7               70              35              17      4       2

This is pretty neat, because we've been able to develop a fairly sophisticated model which would be rather hard to represent in a spreadsheet (particularly the backpressure of the stock maximums), and were able to do it in a couple of minutes.

If you want to do some graphing on these outputs, you can export them as CSV

cat hiring-blog.txt | systems-run --csv

Which will get results like:

,Recruiters,Prospect,PhoneScreen,Onsite,Offer,Accept
0,3,0,0,0,0,0
1,4,30,0,0,0,0
2,5,40,15,0,0,0
3,6,50,20,7,0,0
4,7,60,25,10,1,0
5,7,70,30,12,3,0
6,7,70,35,15,3,1
7,7,70,35,17,3,1
8,7,70,35,17,4,1
9,7,70,35,17,4,2
10,7,70,35,17,4,2

There is still a bunch of functionality to add to the underlying library, systems, but I'm pretty excited by the early flexibility and speed of writing.